更新丟失是指併發下兩次更新同時進行,後一次更新覆蓋了前一次更新的狀況,更新丟失是數據沒有保證一致性致使的。
舉個栗子:mysql
讀取
他此時的餘額100,計算新的餘額爲100+50=150讀取
他此時的餘額100,計算新的餘額爲100+50=150寫入
數據庫,以後C操做也將balance=150寫入
數據庫上面的例子,A同時收到兩筆50元轉帳,最後的餘額應該是200元,但卻由於併發的問題變爲了150元,緣由是B和C向A發起轉帳請求時,同時打開了兩個數據庫會話,進行了兩個事務,後一個事務拿到了前一個事務的中間狀態數據,致使更新丟失。
經常使用的解決思路有兩種:sql
顧名思義,悲觀鎖在讀取數據的時候都會認爲會有別人去修改,因而在取數據的時候會對當前數據加一個鎖,在操做結束前,不容許其他操做更改。要注意悲觀鎖和樂觀鎖都是業務邏輯層次的定義,不一樣的設計可能會有不一樣的實現。在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再計算事務