4

我正在使用本地數據庫中的記錄更新遠程服務器上的大量項目的過程。這是僞代碼。存儲過程中循環內的事務

CREATE PROCEDURE UpdateRemoteServer 
    pre-processing 
    get cursor with ID's of records to be updated 
    while on cursor 
     process the item 

無論多少,我們對其進行優化,程序將需要一段時間,所以我們不希望這件事被處理成一個單一的交易。這些項目在處理後會被標記,所以如果過程中斷,應該有可能從中斷。

包裝紙循環的內容(「處理項目」)在開始/提交TRAN不會做的伎倆...看來,整個陳述

EXEC UpdateRemoteServer 

被作爲單個事務處理。我怎樣才能使每個項目的過程作爲一個完整的,單獨的交易?

注意,我很想來運行這些爲「非事務性更新」,但選項僅可(據我所知)於2008年

回答

1

編輯:正如Remus所指出的那樣,遊標不會默認打開一個事務;因此,這不是OP提出的問題的答案。我仍然認爲比光標有更好的選擇,但是這並不能回答問題。

斯圖

原來的答案:

您所描述的具體症狀是由於光標默認情況下打開一個交易的事實,因此,不管你是如何工作的呢,你得一個長時間運行的事務,只要你使用遊標(除非你完全避免鎖定,這是另一個壞主意)。

正如其他人指出的,遊標SUCK。 99.9999%的時間你不需要它們。

你真的有兩個選擇,如果你想在與SQL Server數據庫級別做到這一點:

  1. 使用SSIS執行您的操作;速度非常快,但可能無法滿足您對特定風格的SQL Server的需求。

  2. 由於您正在處理遠程服務器,並且您擔心連接問題,因此可能必須使用循環機制,因此請使用WHILE並一次提交批處理。儘管WHILE與遊標有許多相同的問題(循環仍然吸引SQL),但您可以避免創建外部事務。

斯圖

+0

感謝您的信息。這實際上回答了這個問題。除此之外,我知道遊標吸吮。在這種情況下,它不是導致性能問題*的本身*,而是在循環內完成工作。但這是一個單獨的問題。 – harpo 2009-10-08 18:09:02

+0

我不同意「遊標吸」,但這是一個不同的討論。 – Thorsten 2009-10-08 18:09:33

+0

@IronGoofy:你是對的;遊標是一種專門的工具,應該用在專門的環境中(有點像螺旋提取器;你不需要每天都這樣做,如果你這樣做,你就會做錯事)。說「他們吸」更容易。 :) – 2009-10-08 18:13:22

0

是否喲只從SQL Server中運行這個,或從應用程序?如果是的話,得到待處理的列表,然後在應用程序中循環,只處理所需的子集。

那麼事務應該由你的應用程序進行處理,並應只鎖定項目更新/頁上的項目都在。

+0

不幸的是,這麼做是完全從SQL Server中,並且要求是超出了我的控制。 – harpo 2009-10-08 17:35:57

+0

它是母雞還是處理作爲sigle事務,如果你不是從一個sp執行,而只是簡單的tsql?avaoid sp,並使用inners,將事務綁定到tsql會話? – 2009-10-08 17:39:05

+0

我們認爲,問題在於該流程需要自動化,因此必須通過單個命令才能執行。 – harpo 2009-10-08 17:40:46

0

從來不會在一個循環中一次處理一個項目時,你正在做的事務性工作。您可以循環處理它們的記錄處理組,但一次不會執行一條記錄。而不是基於集合的插入,你的表現會從幾小時變成幾分鐘甚至幾秒鐘。如果您使用遊標來插入更新或刪除,並且每個語句中不處理至少1000個rowa(不是每次一個),那麼您做錯了事情。對於這樣的事情,遊標是一種極其糟糕的做法。

+2

當然,如果可以的話,我會使用基於集合的插入。我一次處理一個項目,因爲每個項目都有一些需要更新的關聯子表。因此,每次迭代必須處理數十條記錄,並且實際上不能重寫爲一次處理整個更新。到目前爲止,我一直拒絕採用壓制這種非常常見的不答案的政策(即「你的整個前提是錯誤的,你不應該這樣做」),但我想知道他們的作者不會將它們放在評論中。 – harpo 2009-10-08 18:05:07

3

EXEC過程確實創建事務。一個非常簡單的測試將顯示此:

create procedure usp_foo 
as 
begin 
    select @@trancount; 
end 
go 

exec usp_foo; 

的@@內usp_foo TRANCOUNT是0,所以EXEC語句不啓動一個隱式事務。如果您在進入UpdateRemoteServer時啓動了一個事務,則表示有人啓動了該事務,但我無法說出是誰。

話雖這麼說,使用遠程服務器和DTC更新項目是要表現相當糟糕。至少其他服務器是否也是SQL Server 2005?也許您可以將請求排隊以更新並在本地和遠程服務器之間使用messaging,並讓遠程服務器根據消息中的信息執行更新。由於兩臺服務器只需要處理本地事務,並且由於排隊消息的鬆散耦合,您可以獲得更好的可用性,因此性能會更好。

更新

光標實際上不會開始交易。典型的基於光標的批處理通常基於光標並批量更新爲特定大小的事務。這對夜間工作非常普遍,因爲它可以實現更好的性能(由於事務規模較大,可以實現日誌刷新吞吐量),並且可以中斷和恢復工作而不會失去永續。批量處理循環的簡化版本通常是這樣的:

create procedure usp_UpdateRemoteServer 
as 
begin 
    declare @id int, @batch int; 
    set nocount on; 
    set @batch = 0; 

    declare crsFoo cursor 
    forward_only static read_only 
    for 
    select object_id 
    from sys.objects; 

    open crsFoo; 

    begin transaction 
    fetch next from crsFoo into @id ; 
    while @@fetch_status = 0 
    begin 

    -- process here 

    declare @transactionId int; 
    SELECT @transactionId = transaction_id 
     FROM sys.dm_tran_current_transaction; 
    print @transactionId; 

    set @batch = @batch + 1 
    if @batch > 10 
    begin 
     commit; 
     print @@trancount; 
     set @batch = 0; 
     begin transaction; 
    end 
    fetch next from crsFoo into @id ; 
    end 
    commit; 
    close crsFoo; 

    deallocate crsFoo; 
end 
go 

exec usp_UpdateRemoteServer; 

我忽略,則錯誤處理部分(開始嘗試/捕獲開始)和花哨的@@ FETCH_STATUS檢查(靜態遊標其實並不需要他們無論如何)。此演示代碼顯示在運行過程中有幾個不同的事務啓動(不同的事務ID)。許多時間批次也deploy transaction savepoints at each item處理,以便他們可以安全地跳過導致異常的項目,使用類似於我的鏈接中的模式,但這不適用於分佈式事務,因爲保存點和DTC不混合。

+0

你的表演完全正確......我希望當我們選擇這種技術時我就知道了。儘管如此,我們現在還不是可以重寫整個事物的地方,所以解決這個問題會使目前的例程可以接受。 – harpo 2009-10-08 18:02:02

+0

剛剛跑過你的測試過程,我不得不同意;遊標顯然不會始終保持事務。我甚至刪除了指定的選項以獲取動態可更新遊標,並且事務仍然發生變化。我會取消我的回答(如果不是我在遊標使用上的位置)。 – 2009-10-08 23:51:20

+0

Remus,糾正我,如果我在這裏錯了,但一旦你開始修改服務器之間的數據(通過鏈接的服務器)不會自動啓動一個隱式的分佈式事務,以便如果遠程系統的更改失敗,失敗可能是正確地發回到調用SQL Server? – mrdenny 2009-10-08 23:56:44

0

只是一個想法..

  • 只處理幾個項目,當程序被調用(如只能獲得TOP 10個項目來處理)
  • 處理這些

希望這將是交易的結束。

然後編寫一個包裝程序,只要有更多工作要做就可以調用該過程(使用簡單的count(..)來查看是否有項目或者過程返回true,指示有更多工作需要處理做的。

不知道這是否正常工作,但也許這個想法是有幫助的。