1、樂觀鎖
樂觀鎖(Optimistic Locking)是一種思想,相對悲觀鎖而言,樂觀鎖認為對同一個數據的並發操作,不會造成沖突,所以在數據提交更新的時候,才會正式對數據進行沖突校驗,如果有沖突,則給用戶返回錯誤的信息,讓用戶決定如何處理。樂觀地認為,不加鎖的並發操作是沒有問題的。
具體是實現思路是,第一次讀的時候,獲取到某個字段值(版本/時間戳),處理完業務邏輯開始更新時,需要再次查看該字段的值是否和第一次的一樣。如果一樣就更新,反之拒絕。之所以叫樂觀,是因為這個模式沒有從數據庫加鎖。
1-1、樂觀鎖的實現方式
1、使用數據版本(Version)記錄機制實現,這是樂觀鎖最常用的一種實現方式。
什么是數據版本?就是在數據中增加一個版本標識,一般是通過給數據庫表增加一個數字類型的“version”字段來實現。
當讀取數據的時候,將 version 字段的值一同讀出,數據每更新一次,對此 version 值+1。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的 version 進行對比,如果數據庫表當前版本號與第一次取出來的 version 值相等,則予以更新,否則認為是過期數據。

如上圖一所示,如果更新操作順序執行,則數據的版本(version)依次遞增,不會產生沖突。但是,如果發生有不同的業務操作對同一版本的數據進行修改,如圖二的用戶2和用戶3同時對 version2 進行修改,那么,先提交的操作(用戶3)會把數據版本 version 更新為2,當用戶2在用戶3之后提交更新時,發現數據版本(version)已經被修改了,則用戶2的更新操作會失敗。代碼示例:
update ccs_inv_current_inv t
set t.stats = 5, t.cersion = t.version + 1
WHERE t.id = #{id}
and t.version = #{version};
2、樂觀鎖的第二種實現方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,隨意命名,字段類型使用時間戳(timestamp)[時間戳類型:行數據更新時,時間會自動更新],和上面的 version 類似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和自己更新前獲取到的時間戳進行對比,如果一致則允許修改,否則就是版本沖突。
●樂觀鎖的實現
1、CAS(Compare And Swap)
2、MVCC(Multi-Version Concurrency Control)
●樂觀鎖的特點
1、並發度高;
2、程序實現,不會發生死鎖;
2、悲觀鎖
悲觀鎖,正如其名,悲觀的,具有強烈的獨占和排他特性。它認為對於同一個數據的並發操作,一定會被修改,哪怕沒有修改,也會認為被修改。因此,在它處理數據前,先對數據進行加鎖,直到它完成修改操作(commit)才釋放鎖。悲觀地認為,不加鎖的並發操作一定會出問題。
悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。
以MySQL的 InnoDB 為例,預設的 Transaction isolation level 為 REPEATABLE READ。【要使用悲觀鎖,我們必須關閉mysql數據庫的自動提交屬性,set autocommit=0】
● Select 的讀取鎖有兩種方式:
- Select ...... Lock In Share Mode;
- Select ...... For Update;
兩種方式在事務(Transaction)進行中 Select 到同一個數據表時,都必須等待其他事務數據被(commit)之后才會執行。而兩者主要的區別在於,Lock In Share Mode 在有一方事務要 Update 同一個表單時,很容易造成死鎖。
簡單地說,如果 Select 后面若要 Update 同一個表單,最好使用 Select ...... For Update。
● MySQL Select ... For Update 的 Row Lock(行鎖) 和 Table Lock(表鎖)。
由於 InnoDB 預設是 Row-Level Lock,所以只有【明確】的指定主鍵,MySQL才會執行 Row Lock(只鎖住被選取的數據),否則 MySQL 將會執行 Table Lock(將整個表鎖住)。
- 明確指定主鍵,並且有此數據,Row Lock;
- 明確指定主鍵,若查無此數據,無 Lock;
- 無主鍵/主鍵不明確,Table Lock;
● 除了主鍵外,使用索引也會影響數據庫的鎖定級別
- 明確指定索引,並且有此數據,Row Lock;
- 明確指定索引,如查無此數據,無 Lock;
● 悲觀鎖的特點
1、並發度小;
2、依靠數據實現;
3、各自適用場景
悲觀鎖和樂觀鎖是數據庫用來保證數據並發安全,防止更新丟失的兩種方法,兩者大部分場景下差異不大,一些獨特場景下有一些差別,一般我們可以從以下幾方面來分析:
- 響應速度:如果需要非常高的響應速度,建議采用樂觀鎖方案,成功就執行,不成功就失敗,不需要等待其他並發去釋放鎖;
- 沖突頻率:如果沖突頻率非常高,建議采用悲觀鎖,保證成功率,如果沖突頻率大,樂觀鎖會需要重試多次才能成功,代價比較大;
- 重試代價:如果重試代價大,建議采用悲觀鎖;
從各自的優缺點來看,我們可以認為,悲觀鎖適合寫操作非常多的場景,樂觀鎖適合讀操作非常多的場景,因為不加鎖的話,性能會有大幅度的提升。
小結
兩種鎖機制區別不大,我們明白其中的原理,運用起來就不會有太大困難。
在實際應用中,我們也要充分考慮各自的優缺點,選擇合理、可行的方案。
