MYSQL使用鎖解決併發下的更新丟失問題

更新丟失是指併發下兩次更新同時進行,後一次更新覆蓋了前一次更新的狀況,更新丟失是數據沒有保證一致性致使的。
舉個栗子:mysql

  • 用戶A在銀行卡有100元錢,某一刻用戶B向A轉帳50元(稱爲B操做),同時有用戶C向A轉帳50元(稱爲C操做);
  • B操做從數據庫中讀取他此時的餘額100,計算新的餘額爲100+50=150
  • C操做也從數據庫中讀取他此時的餘額100,計算新的餘額爲100+50=150
  • B操做將balance=150寫入數據庫,以後C操做也將balance=150寫入數據庫
  • 最終A的餘額變爲150

上面的例子,A同時收到兩筆50元轉帳,最後的餘額應該是200元,但卻由於併發的問題變爲了150元,緣由是B和C向A發起轉帳請求時,同時打開了兩個數據庫會話,進行了兩個事務,後一個事務拿到了前一個事務的中間狀態數據,致使更新丟失。
經常使用的解決思路有兩種:sql

  • 加鎖同步執行
  • update前檢查數據一致性

悲觀鎖

顧名思義,悲觀鎖在讀取數據的時候都會認爲會有別人去修改,因而在取數據的時候會對當前數據加一個鎖,在操做結束前,不容許其他操做更改。要注意悲觀鎖和樂觀鎖都是業務邏輯層次的定義,不一樣的設計可能會有不一樣的實現。在mysql層經常使用的悲觀鎖實現方式是加一個排他鎖。
排他鎖
查閱資料不少對排他鎖的解釋是:「排他鎖經過在事務中使用select xx for update語句來實現,排他鎖會在當前行加一個行級鎖,在當前事務提交前,其他事務沒法進行update操做。」數據庫

然而實際上並非這樣,其實是加了排他鎖的數據,在釋放鎖(事務結束)以前其餘事務不能再對該數據加鎖
排他鎖之因此能阻止update,delete等操做是由於update,delete操做會自動加排他鎖

也就是說即便加了排他鎖也沒法阻止select操做。而select XX for update 語法能夠對select 操做加上排他鎖。因此爲了防止更新丟失能夠在select時加上for update加鎖 這樣就能夠阻止其他事務的select for update(但注意沒法阻止select)
example:併發

begin;
select * from account where id = 1 for update;
update account set balance=150 where id =1;
commit;

這樣在B操做提交前,C操做沒法得到排他鎖,從而避免對account的重複更新致使的更新丟失。設計

樂觀鎖

樂觀鎖是指在獲取數據時候不加鎖,樂觀的認爲操做不會有衝突,在update的時候再去檢查衝突。
example:code

begin;
select balance from account where id=1;
-- 獲得balance=100;而後計算balance=100+50=150
update account set balance = 150 where id=1 and balance = 100;
commit;

如上,若是sql在執行的過程當中發現update的affected爲0 說明balance不等於100即該條數據有被其他事務更改過,此時業務上就能夠返回失敗或者從新select再計算事務

相關文章
相關標籤/搜索