1. 程式人生 > >悲觀鎖和樂觀鎖

悲觀鎖和樂觀鎖

mysql

鎖有兩種機制:悲觀鎖和樂觀鎖。


悲觀鎖

悲觀鎖,鎖如其名,他對世界是悲觀的,他認為別人訪問正在改變的數據的概率是很高的,所以從數據開始更改時就將數據鎖住,直到更改完成才釋放。


一個典型的倚賴數據庫的悲觀鎖調用:

select * from account where name=”Erica” for update

這條 sql 語句鎖定了 account 表中所有符合檢索條件( name=”Erica” )的記錄。 本次事務提交前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。該語句用來鎖定特定的行(如果有where 子句,就是滿足 where條件的那些行)。當這些行被鎖定後,其他會話可以選擇這些行,但不能更改或刪除這些行,直到該語句的事務被 commit 語句或 rollback 語句結束為止。需要註意的是,select ....for update 要放到 mysql 的事務中,即 begin 和commit 中,否則不起作用。


悲觀鎖可能會造成加鎖的時間很長,並發性不好,特別是長事務,影響系統的整體性能。

悲觀鎖的實現方式:

悲觀鎖,也是基於數據庫的鎖機制實現。傳統的關系型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。


樂觀鎖


樂觀鎖,他對世界比較樂觀,認為別人訪問正在改變的數據的概率是很低的,所以直到修改完成準備提交所做的修改到數據庫的時候才會將數據鎖住,當你讀取以及改變該對象時並不加鎖,完成更改後釋放。樂觀鎖不能解決臟讀的問題。

樂觀鎖加鎖的時間要比悲觀鎖短,大大提升了大並發量下的系統整體性能表現。

樂觀鎖的實現方式:

1.大多是基於數據版本

(Version)記錄機制實現,需要為每一行數據增加一個版本標識(也就是每一行數據多一個字段 version),每次更新數據都要更新對應的版本號+1。

工作原理:讀出數據時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交數據的版本信息與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大於數據庫表當前版本號,則予以更新,否則認為是過期數據,不得不重新讀取該對象並作出更改。


假設數據庫中帳戶信息表中有一個version 字段,當前值為 1 ;而當前帳戶余額字段( balance )為 $100 。

1 操作員 A 此時將其讀出( version=1 ),並從其帳戶余額中扣除 $5( $100-$50 )。

2 在操作員 A 操作的過程中,操作員 B 也讀入此用戶信息( version=1 ),並從其帳戶余額中扣除 $20 ( $100-$20 )。

3 操作員 A 完成了修改工作,將數據版本號加一( version=2 ),連同帳戶扣除後余額(balance=$50 ),提交至數據庫更新,此時由於提交數據版本大於數據庫記錄當前版本,數據被更新,數據庫記錄 version 更新為 2 。

4 操作員 B 完成了操作,也將版本號加一( version=2 )試圖向數據庫提交數據( balance=$80 ),但此時比對數據庫記錄版本時發現,操作員 B 提交的數據版本號為 2 ,數據庫記錄當前版本也為 2,不滿足 “ 提交版本必須大於記錄當前版本才能執行更新 “ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。

這樣,就避免了操作員 B 用基於 version=1 的舊數據修改的結果覆蓋操作員 A 的操作結果的可能。


從上面的例子可以看出,樂觀鎖機制避免了長事務中的數據庫加鎖開銷(操作員 A 和操作員 B 操作過程中,都沒有對數據庫數據加鎖),大大提升了大並發量下的系統整體性能表現。

2.使用時間戳來實現

同樣是在需要樂觀鎖控制的 table 中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的 version 類似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和自己更新前取到的時間戳進行對比,如果一致則 OK,否則就是版本沖突。


悲觀鎖和樂觀鎖的適用場景:

如果並發量不大,可以使用悲觀鎖解決並發問題;但如果系統的並發量非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法.現在大部分應用都應該是樂觀鎖的。

本文出自 “秦斌的博客” 博客,謝絕轉載!

悲觀鎖和樂觀鎖