悲觀鎖的問題:mysql
由於悲觀鎖大多數狀況下依靠數據庫的鎖機制實現,以保證操做最大程度的獨佔性。若是加鎖的時間過長,其餘用戶長時間沒法訪問,影響了程序的併發訪問性,同時這樣對數據庫性能開銷影響也很大,特別是對長事務而言,這樣的開銷每每沒法承受。因此與悲觀鎖相對的,咱們有了樂觀鎖。sql
樂觀鎖的原理大體同樣,這裏我提供兩種思路:數據庫
1.使用數據版本(Version)記錄機制實現,經過爲數據庫表增長一個數字類型的 「version」 字段來實現。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對此version值加一。當咱們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的version值進行比對,若是數據庫表當前版本號與第一次取出來的version值相等,則予以更新。mybatis
2.樂觀鎖定的第二種實現方式和第一種差很少,一樣是在須要樂觀鎖控制的table中增長一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version相似,也是在更新提交的時候檢查當前數據庫中數據的時間戳和本身更新前取到的時間戳進行對比,若是一致則OK,不然就是版本衝突。併發
下面以mybatis 舉例說明,關鍵代碼以下:框架
銀行兩操做員同時操做同一帳戶就是典型的例子。
好比A、B操做員同時讀取一餘額爲1000元的帳戶,A操做員爲該帳戶增長100元,B操做員同時爲該帳戶扣除50元,A先提交,B後提交。最後實際帳戶餘額爲1000-50=950元,但本該爲1000+100-50=1050。這就是典型的併發問題。性能
樂觀鎖機制在必定程度上解決了這個問題。樂觀鎖,大可能是基於數據版本(Version)記錄機制實現。何謂數據版本?即爲數據增長一個版本標識,在基於數據庫表的版本解決方案中,通常是經過爲數據庫表增長一個 「version」 字段來實現。this
讀取出數據時,將此版本號一同讀出,以後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,若是提交的數據版本號大於數據庫表當前版本號,則予以更新,不然認爲是過時數據。spa
對於上面修改用戶賬戶信息的例子而言,假設數據庫中賬戶信息表中有一個version字段,當前值爲1;而當前賬戶餘額字段(balance)爲1000元。假設操做員A先更新完,操做員B後更新。
a、操做員A此時將其讀出(version=1),並從其賬戶餘額中增長100(1000+100=1100)。
b、在操做員A操做的過程當中,操做員B也讀入此用戶信息(version=1),並從其賬戶餘額中扣除50(1000-50=950)。
c、操做員A完成了修改工做,將數據版本號加一(version=2),連同賬戶增長後餘額(balance=1100),提交至數據庫更新,此時因爲提交數據版本大於數據庫記錄當前版本,數據被更新,數據庫記錄version更新爲2。
d、操做員B完成了操做,也將版本號加一(version=2)試圖向數據庫提交數據(balance=950),但此時比對數據庫記錄版本時發現,操做員B提交的數據版本號爲2,數據庫記錄當前版本也爲2,不知足 「提交版本必須大於記錄當前版本才能執行更新 「的樂觀鎖策略,所以,操做員B的提交被駁回。
這樣,就避免了操做員B用基於version=1的舊數據修改的結果覆蓋操做員A的操做結果的可能。.net
操做員A操做以下:
select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1000, version=1 update account set balance=balance+100, version=version+1 where id="1" and version=1 select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1100, version=2
操做員B操做以下:
select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1000, version=1 #操做員A已修改爲功,實際account.balance=1100、account.version=2,操做員B也將版本號加一(version=2)試圖向數據庫提交數據(balance=950),但此時比對數據庫記錄版本時發現,操做員B提交的數據版本號爲2,數據庫記錄當前版本也爲2,不知足 「提交版本必須大於記錄當前版本才能執行更新 「的樂觀鎖策略,所以,操做員B的提交被駁回。 update account set balance=balance-50, version=version+1 where id="1" and version=1 select id, balance, version from account where id="1"; 查詢結果:id=1, balance=1100, version=2
Hibernate、JPA等ORM框架或者實現,是使用版本號,再判斷UPDATE後返回的數值