0. 併發衝突的示例
單使用者的系統現在應該比較罕見了,一般系統都會有很多使用者在同時進行操作;在多使用者系統中,涉及到的乙個普遍問題:當多個使用者「同時」更新(修改或者刪除)同一條記錄時,該如何更新呢?
下圖展示了開放式併發衝突的乙個示例:
假設資料庫中有一條記錄record(以下簡寫為),a、b兩個使用者按照如下順序操作這一條記錄:
(1). a讀取該記錄,取得的值為,讀取完畢後,不對該記錄加排他鎖;
(2). b讀取該記錄,取得的值也為,讀取完畢後,不對該記錄加排他鎖;
(3). b將該記錄修改為,並寫回資料庫;由於該記錄沒有被其他使用者鎖定,且b在修改時該記錄的值與第(2)步中讀取的值一致,因此可以正常寫入資料庫;
(4). a將該記錄修改為,並寫回資料庫;由於a在修改時該記錄的值已更新為,與第(1)步中讀取的值不一致,因此引發併發衝突;
1. 開放式併發(樂觀併發) & 封閉式併發(悲觀併發)
開始之前,先介紹兩個概念(from 《sql server 2008聯機叢書》)。
樂觀併發控制
在樂觀併發控制中,使用者讀取資料時不鎖定資料。當乙個使用者更新資料時,系統將進行檢查,檢視該使用者讀取資料後其他使用者是否又更改了該資料。如果其他使用者更新了資料,將產生乙個錯誤。一般情況下,收到錯誤資訊的使用者將回滾事務並重新開始。這種方法之所以稱為樂觀併發控制,是由於它主要在以下環境中使用:資料爭用不大且偶爾回滾事務的成本低於讀取資料時鎖定資料的成本。
悲觀併發控制
乙個鎖定系統,可以阻止使用者以影響其他使用者的方式修改資料。如果使用者執行的操作導致應用了某個鎖,只有這個鎖的所有者釋放該鎖,其他使用者才能執行與該鎖衝突的操作。這種方法之所以稱為悲觀併發控制,是因為它主要用於資料爭用激烈的環境中,以及發生併發衝突時用鎖保護資料的成本低於回滾事務的成本的環境中。
舉個例子來說:
樂觀併發:本文起始位置的,展示的是樂觀併發情況下引發的衝突。
悲觀併發:繼續沿用中示例的請求順序,如果第(1)步中a讀取這條記錄後,給記錄加上排他鎖,並且一直持有,在更新完畢之前不釋放該鎖;則第(2)步中b嘗試讀取該記錄時,請求會被阻塞,直到a釋放該記錄,或者請求超時。因此新的執行順序(假設請求沒有超時)為:a讀取並鎖定記錄-->b嘗試讀取該記錄[b被阻塞等待]-->a寫回到資料庫,並釋放該記錄-->b讀取到,並鎖定該記錄-->b修改該記錄並寫回資料庫……
2. linq to sql中的樂觀併發控制
l2s支援開放式併發控制。使用l2s執行修改和刪除操作時,同時開啟sql server profile來檢視生成的sql**,我們可以看到類似這樣的**(假設表上加了timestamp欄位):
update tablename set field1=@p0, field2=@p1 where primarykey=@p2 and timestampfield=@p3
//(省略)....
delete tablename where primarykey=@p0 and timestampfield=@p1
//(省略)....
當記錄被更新時,則其timestamp欄位會被自動更新。因此,如果在使用者讀取該記錄後、且更新該記錄之前,有其他使用者更新過這條記錄,則更新會失敗(根據受影響行數為0來判斷),l2s會丟擲changeconflictexception異常。
以上描述的是表上有加timestamp欄位的情況,如果表上沒有timestamp欄位,l2s會對對映為updatecheck = updatecheck.always 或 updatecheck.whenchanged的字段成員進行開放式併發檢查,可以根據sql server profile來檢視,不再贅述。
3. 衝突解決
既然有了衝突,就需要把衝突給和諧掉。
還是以本文起始位置的例子來說,最後a更新時,該更新為啥呢?可以存在如下三種選擇:
(1). 覆蓋資料值庫:?
(2). 保留資料庫值:?
(3). 合併為:?
這三種方式分別對了l2s的三種解決方案:
3.1 通過覆蓋資料庫值解決併發衝突
try
catch (changeconflictexception e)
}
db.submitchanges(conflictmode.failonfirstconflict); //處理完衝突後,重試
3.2 通過保留資料庫值解決併發衝突
try
catch (changeconflictexception e)
}
db.submitchanges(conflictmode.failonfirstconflict); //處理完衝突後,重試
3.3 通過與資料庫值合併解決併發衝突
try
catch (changeconflictexception e)
}
db.submitchanges(conflictmode.failonfirstconflict); //處理完衝突後,重試
Linq to Sql 併發衝突及處理策略
原文 linq to sql 併發衝突及處理策略 1 通過覆蓋資料庫值解決併發衝突 try catch changeconflictexception e 2 通過保留資料庫值解決併發衝突 try catch changeconflictexception e db.submitchanges 處理...
Linq to sql 檢測併發
首先使用下面的 sql 語句查詢資料庫的產品表 select from products where categoryid 1 為了看起來清晰,我已經事先把所有分類為 1 產品的 和庫存修改為相同值了。然 後執行下面的程式 varquery from p in ctx.products where ...
LINQ TO SQL 併發控制
column特性的updatacheck用於設定併發衝突處理方式 always 使用使用這個列進行衝突檢測 never 從不使用這個列進行衝突檢測 whenchanged 僅在成員被應用程式更改時使用這個列檢測 這裡的檢測指傳送的sql中的where中的條件,如userinfo表中name列進行衝突...