技術(shù)詳解:三招優(yōu)化.NET中的鎖(組圖)
在這篇文章中,我將使用三個方法處理樂觀鎖,包括ADO.NET數(shù)據(jù)集、SQL Server時間戳數(shù)據(jù)類型和新舊值檢查,首先我們從并發(fā)談起,探討5個并發(fā)問題,然后從實際出發(fā),利用這三種方法實現(xiàn)樂觀鎖。
為什么需要鎖?
在多用戶環(huán)境中,大家同時更新相同的記錄可能會引發(fā)沖突,這個問題用專業(yè)的術(shù)語描述就叫做并發(fā)性。并發(fā)會造成什么樣的沖突?并發(fā)主要會導(dǎo)致四種常見的問題,詳細(xì)情況請看下表。
如何解決上述沖突?
答案是使用樂觀鎖或悲觀鎖,下面將進(jìn)一步進(jìn)行闡述。什么是樂觀鎖?顧名思義,樂觀鎖假設(shè)多個事務(wù)相互不會影響對方,換句話說就是,在樂觀鎖模式下,沒有鎖操作會得到執(zhí)行,事務(wù)只是驗證是否有其它事務(wù)修改數(shù)據(jù),如果有則進(jìn)行事務(wù)回滾,否則就提交。
樂觀鎖是如何工作的?
1、實現(xiàn)樂觀鎖的方法有多種,但基本原則都一樣,總是少不了下面五個步驟:
2、記錄當(dāng)前的時間戳
3、開始修改值
4、在更新前,檢查是否有其他人更新了值(通過檢查新舊時間戳實現(xiàn))
5、如果不相等就回滾,否則就提交
圖 1 樂觀鎖的工作原理
實現(xiàn)樂觀鎖的解決方案
在.NET中,實現(xiàn)樂觀鎖的方法主要有三種:
1、數(shù)據(jù)集(Dataset):數(shù)據(jù)集是實現(xiàn)樂觀鎖的默認(rèn)方法,在更新前它會檢查新舊值。
2、時間戳數(shù)據(jù)類型(timestamp):在你的表中創(chuàng)建一個timestamp數(shù)據(jù)類型,在更新時,檢查舊時間戳是否等于新時間戳。
3、直接檢查新舊值:在更新時檢查舊值和新值是否相等,如果不相等就回滾,否則就提交。
解決方案1:數(shù)據(jù)集
正如前面所說的,數(shù)據(jù)集是處理樂觀鎖的默認(rèn)方法,下面是一個簡單的快照,在Adapter的update函數(shù)上有一個調(diào)試點,當(dāng)我移除斷點運行update函數(shù)時,它拋出如下圖所示的并行異常錯誤。
圖 2 Update函數(shù)執(zhí)行時拋出的異常錯誤
如果你運行后端分析器,你將會看到更新語句檢查當(dāng)前值和舊值是否相等:
在這種情況下,我嘗試將“AuthorName”字段值修改為“This is new”,但更新時會檢查舊值“This is old author”,下面是比較舊值的精簡代碼段:
解決方案2:使用timestamp數(shù)據(jù)類型
SQL Server有一個數(shù)據(jù)類型是timestamp,它是實現(xiàn)樂觀鎖的另一種途徑,每次更新SQL Server數(shù)據(jù)時,時間戳?xí)詣赢a(chǎn)生一個***的二進(jìn)制數(shù)值,時間戳數(shù)據(jù)類型可用來版本化你的記錄更新。
圖 3 timestamp數(shù)據(jù)類型
為了實現(xiàn)樂觀鎖,首先需要取得舊的時間戳值,在更新時檢查舊的時間戳值是否等于當(dāng)前時間戳,如:
然后檢查是否發(fā)生了更新操作,如果沒有發(fā)生更新,則使用SQL Server的raiserror產(chǎn)生一系列錯誤消息。
如果發(fā)生了并發(fā)沖突,當(dāng)你如下圖所示這樣調(diào)用ExecuteNonQuery時,你應(yīng)該會看到錯誤傳播。
圖 4 時間戳發(fā)生變化,存儲過程產(chǎn)生了錯誤
解決方案3:檢查舊值和新值
許多時候,我們只需要檢查相關(guān)字段值的一致性,其它字段則可以忽略,在update語句中,我們可以直接做這種比較。