樂觀鎖和悲觀鎖的區別以及應用場景

悲觀鎖(Pessimistic Lock)

顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。

 

樂觀鎖(Optimistic Lock)

顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。

 

兩種鎖各有優缺點,不可認爲一種好於另一種,像樂觀鎖適用於寫比較少的情況下,即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果經常產生衝突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。本質上,數據庫的樂觀鎖做法和悲觀鎖做法主要就是解決下面假設的場景,避免丟失更新問題:

 

一個比較清楚的場景

下面這個假設的實際場景可以比較清楚的幫助我們理解這個問題:假設噹噹網上用戶下單買了本書,這時數據庫中有條訂單號爲001的訂單,其中有個status字段是’有效’,表示該訂單是有效的;

後臺管理人員查詢到這條001的訂單,並且看到狀態是有效的用戶發現下單的時候下錯了,於是撤銷訂單,假設運行這樣一條SQL: update order_table set status = ‘取消’ where order_id = 001;

後臺管理人員由於在b這步看到狀態有效的,這時,雖然用戶在c這步已經撤銷了訂單,可是管理人員並未刷新界面,看到的訂單狀態還是有效的,於是點擊”發貨”按鈕,將該訂單發到物流部門,同時運行類似如下SQL,將訂單狀態改成已發貨:update order_table set status = ‘已發貨’ where order_id = 001

觀點1:只有衝突非常嚴重的系統才需要悲觀鎖;“所有悲觀鎖的做法都適合於狀態被修改的概率比較高的情況,具體是否合適則需要根據實際情況判斷。”,表達的也是這個意思,不過說法不夠準確;的確,之所以用悲觀鎖就是因爲兩個用戶更新同一條數據的概率高,也就是衝突比較嚴重的情況下,所以才用悲觀鎖。

觀點2:最後提交前作一次select for update檢查,然後再提交update也是一種樂觀鎖的做法,的確,這符合傳統樂觀鎖的做法,就是到最後再去檢查。但是wiki在解釋悲觀鎖的做法的時候,’It is not appropriate for use in web application development.’, 現在已經很少有悲觀鎖的做法了,所以我自己將這種二次檢查的做法也歸爲悲觀鎖的變種,因爲這在所有樂觀鎖裏面,做法和悲觀鎖是最接近的,都是先select for update,然後update

在實際應用中我們在更新數據的時候,更嚴謹的做法是帶上更新前的“狀態”,如

update order_table set status = ‘取消’ where order_id = 001 and status = ‘待支付’ and ..........;

update order_table set status = ‘已發貨’ where order_id = 001 and status = ‘已支付’ and ..........;

然後在業務邏輯代碼裏判斷更新的記錄數,爲0說明數據庫已經被更新了,否則是正常的。

 

下篇文章說說樂觀鎖的實現,歡迎關注哈

 

-- 思維導圖總結

 

--(完)喜歡就轉發 --

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章