2010-04-17 73 views
4

假設,我即將開始使用ASP.NET和SQL Server 2005的項目。我必須爲此應用程序設計併發需求。我打算在每個表格中添加一個TimeStamp列。在更新表格時,我將檢查TimeStamp列是否與選定的相同。Sql transactrion的併發處理

這種方法是否足夠?或者在任何情況下這種方法都有缺點嗎?

請指教。

感謝

Lijo

+1

可能不是一個好主意 - 看看這篇文章http://www.mssqltips.com/tip.asp?tip=1501 – 2010-04-17 13:12:13

+0

謝謝。它提供了一些亮點。一個問題 - 爲什麼我們說在悲觀條件下使用鎖定比時間戳更好?假設user1讀取它並去了他的辦公室,其他用戶在得到錯誤信息之前必須等很長時間,對吧?如果我們使用時間戳方法,我們不能得到即時回覆? 這裏缺少的鏈接是什麼? – Lijo 2010-04-17 13:38:44

+0

「悲觀條件」意味着鎖定 - 這就是爲什麼很多人知道他們在做什麼使用它 – 2010-04-17 13:43:29

回答

4

的首先,你在你的問題描述的方式在我看來與MS SQL ASP.NET應用程序的數據庫的最佳方式。數據庫中沒有鎖定。它與永久斷開連接的客戶端類似於Web客戶端是完美的。

如何從一些答案中讀到,在術語中存在誤解。我們都指使用Microsoft SQL Server 2008或更高版本來保存數據庫。如果您在MS SQL Server 2008文檔主題「rowversion(的Transact-SQL)」,在打開,你會發現以下幾點:

時間戳對於 rowversion數據類型的同義詞,並受 數據類型同義詞的行爲。「...... 」的時間戳語法棄用。 此功能將在 未來版本的Microsoft SQL 服務器中刪除。避免在 新的開發工作中使用該功能,並計劃 修改當前使用 此功能的應用程序。」

所以時間戳數據類型爲MS SQL的rowversion數據類型的同義詞。它擁有64位,其在每個數據庫內部存在並且可以被看作是@@ DBTS。一個行的數據庫中的一個表的修改之後,計數器將被增加。

正如我讀取計數器你的問題我讀「時間戳」作爲類型rowversion數據的列名。我個人更喜歡名字RowUpdateTimeStamp。在AzManDB中(請參閱以數據庫爲商店的Microsoft授權管理器),我可以看到這樣的名稱。有時也用於ChildUpdateTimeStamp跟蹤分層RowUpdateTimeStamp結構(相對於觸發)。

我實現了這個辦法,我的最後一個項目,並很高興。一般來說,你做以下:

  1. 添加RowUpdateTimeStam p柱到你的數據庫的每個表類型rowversion(它會在Microsoft SQL Management Studio中被視爲時間戳,這是一樣的) 。
  2. 你應該建立你的SQL SELECT查詢發送結果到客戶端的話,你與主數據一起發送額外RowVersion值。如果你有縫一個SELECT,你應該發送的最大RowUpdateTimeStamp價值RowVersion兩個表像
SELECT s.Id AS Id 
    ,s.Name AS SoftwareName 
    ,m.Name AS ManufacturerName 
    ,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp 
      THEN s.RowUpdateTimeStamp 
      ELSE m.RowUpdateTimeStamp 
    END AS RowUpdateTimeStamp 
FROM dbo.Software AS s 
    INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id 

或進行數據鑄像下面

SELECT s.Id AS Id 
    ,s.Name AS SoftwareName 
    ,m.Name AS ManufacturerName 
    ,CASE WHEN s.RowUpdateTimeStamp > m.RowUpdateTimeStamp 
      THEN CAST(s.RowUpdateTimeStamp AS bigint) 
      ELSE CAST(m.RowUpdateTimeStamp AS bigint) 
    END AS RowUpdateTimeStamp 
FROM dbo.Software AS s 
    INNER JOIN dbo.Manufacturer AS m ON s.Manufacturer_Id=m.Id 

舉行RowUpdateTimeStampBIGINT,其對應ULONG數據類型的C#。如果您從多個表中創建OUTER JOINT或JOINT,則所有表格中的構造MAX(RowUpdateTimeStamp)將顯得稍微複雜一些。因爲MS SQL不支持像MAX(A,B,C,d,E)對應的構建函數可以看起來像以下:

(SELECT MAX(rv) 
FROM (SELECT table1.RowUpdateTimeStamp AS rv 
     UNION ALL SELECT table2.RowUpdateTimeStamp 
     UNION ALL SELECT table3.RowUpdateTimeStamp 
     UNION ALL SELECT table4.RowUpdateTimeStamp 
     UNION ALL SELECT table5.RowUpdateTimeStamp) AS maxrv) AS RowUpdateTimeStamp 
  • 所有斷開客戶機(網絡客戶端)不僅接收和保存一些數據行,而且還保存數據行的RowVersion(類型ulong)。
  • 在試圖修改斷開客戶端的數據時,客戶端應該將RowVersion對應的原始數據發送給服務器。該spSoftwareUpdate存儲過程可能看起來像
  • CREATE PROCEDURE dbo.spSoftwareUpdate 
        @Id int, 
        @SoftwareName varchar(100), 
        @originalRowUpdateTimeStamp bigint, -- used for optimistic concurrency mechanism 
        @NewRowUpdateTimeStamp bigint OUTPUT 
    AS 
    BEGIN 
        -- SET NOCOUNT ON added to prevent extra result sets from 
        -- interfering with SELECT statements. 
        -- ExecuteNonQuery() returns -1, but it is not an error 
        -- one should test @NewRowUpdateTimeStamp for DBNull 
        SET NOCOUNT ON; 
    
        UPDATE dbo.Software 
        SET Name = @SoftwareName 
        WHERE Id = @Id AND RowUpdateTimeStamp <= @originalRowUpdateTimeStamp 
    
        SET @NewRowUpdateTimeStamp = (SELECT RowUpdateTimeStamp 
                FROM dbo.Software 
                WHERE (@@ROWCOUNT > 0) AND (Id = @Id)); 
    END 
    

    dbo.spSoftwareDelete存儲過程的代碼看起來像一樣。如果您未打開NOCOUNT,則可以生成DBConcurrencyException在情景中會自動生成很多。 Visual Studio爲您提供了使用樂觀併發的可能性,如TableAdapterDataAdapter的高級選項中的「使用樂觀併發」複選框。

    如果你看看dbo.spSoftwareUpdate存儲過程carful你會發現,我使用RowUpdateTimeStamp <= @originalRowUpdateTimeStamp在WHERE而不是RowUpdateTimeStamp = @originalRowUpdateTimeStamp。我這樣做是因爲,具有客戶端的@originalRowUpdateTimeStamp的值通常是從多個表格構建爲MAX(RowUpdateTimeStamp)。所以它可以是RowUpdateTimeStamp < @originalRowUpdateTimeStamp。要麼你應該使用嚴格的相等性=並且在這裏重現與您在SELECT語句中使用的相同的複雜JOIN語句,或者使用< =像我這樣構建並保持與以前完全相同的安全性。

    順便說一下,基於RowUpdateTimeStamp,可以爲數據創建非常好的價值ETag它可以在HTTP頭與數據一起發送到客戶端。通過使用ETag,您可以在客戶端實施智能數據緩存。

    我不能在這裏寫完整的代碼,但你可以在互聯網上找到很多例子。我只想重申,在我看來,使用樂觀併發基於rowversion一次在大多數的情況下ASP.NET的最佳途徑。

    +1

    但是,感謝您的詳細答案,存儲過程代碼看起來不正確或不完整。 WHERE Id = @ Id AND RowUpdateTimeStamp 0)?我知道這已經3年了,但我只是偶然發現了這一點。 – Mike 2013-02-06 01:50:11

    +0

    @Mike:謝謝,這是一箇舊的「剪切和粘貼錯誤」,錯誤修改了代碼!我更新了STORED PROCEDURE'dbo.spSoftwareUpdate'的代碼。我希望現在的代碼是正確的。 – Oleg 2013-02-07 19:24:34

    1

    我不知道是應該併發在這樣的數據庫進行處理。數據庫本身應該能夠管理隔離和事務行爲,但是線程行爲應該在代碼中完成。

    +0

    他沒有提到穿線。數據庫必須能夠處理併發訪問 - 無論這些來自單個多線程應用程序還是來自多個應用程序實例,或者兩者都不在此處,也不在此處。 – 2010-04-17 13:21:39

    +0

    然後,他需要關注自己的酸性和孤立性。 – duffymo 2010-04-17 14:06:49

    2

    在SQL Server中,針對某種情況的推薦方法是創建一個類型爲'rowversion'的列,並使用它來檢查該行中是否有任何字段已更改。

    SQL Server保證如果行中的任何值發生更改(或插入新行),則rowversion列會自動更新爲不同的值。讓數據庫爲你處理這個比你自己做的更可靠。

    在您的更新語句中,您只需添加一個where子句來檢查rowversion值是否與您第一次檢索該行時的值相同。如果不是這樣,別人已經改變了行(即:它的骯髒)

    而且,從該頁面:

    時間戳語法已被棄用。 此功能將在 未來版本的Microsoft SQL 服務器中刪除。避免在 新開發工作中使用此功能,並計劃到 修改當前使用 此功能的應用程序。

    0

    排版建議是正確的我會說,但它令人失望地看到時間戳將很快被棄用。我的一些舊應用程序因爲不同的原因而使用它,然後檢查併發性。