樂觀鎖 -業務判斷 解決高併發問題
在解決高併發問題時,如果是分散式系統顯然我們只能夠使用資料庫端加鎖機制來解決這個問題,但是這種同步機制或者資料庫物理鎖機制會犧牲一部分的效能,所以常常以另外一種方式來解決這個問題 就是樂觀鎖模式
銀行兩操作員同時操作同一賬戶就是典型的樂觀鎖模式。
比如A、B操作員同時讀取一餘額為1000元的賬戶,A操作員為該賬戶增加100元,B操作員同時為該賬戶扣除50元,A先提交,B後提交。最後實際賬戶餘額為1000-50=950元,但本該為1000+100-50=1050。這就是典型的併發問題。
樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基於資料版本(Version)記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 “version” 欄位來實現。
讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。
對於上面修改使用者帳戶資訊的例子而言,假設資料庫中帳戶資訊表中有一個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的操作結果的可能。
操作員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後返回的數值