「樂觀鎖」這個詞以前我也沒聽過。上次在測試需求的時候,查詢資料庫發現有一個version欄位,於是請教開發這個字幹嘛使,人家回復我:樂觀鎖,解決並發更新用的。當時大家都忙,咱也不敢多問。
今天就來折騰一下「樂觀鎖」。
一、什麼是樂觀鎖
樂觀鎖其實用一句話來形容其作用就是:當要更新一條記錄的時候,希望這條記錄沒有被別人更新,從而實現線程安全的數據更新。
結合下場景,記得那是一張庫存表,有一個欄位記錄商品庫存,涉及多個地方都有可能去更新它:
程序A 查詢到了這條數據,得到庫存是800,準備+200更新成1000,但是還沒更新。程序B 也查詢到了這條數據,得到庫存是800,準備-200更新成600,並且提交更新了。那麼,這時候A再提交更新之後,B就會發現明明是自己是800-200=600,怎麼最後變成了1000?
這就是因為A的事務導致了B的數據更新丟失。
文字可能讀起來比較晦澀,有請靈魂畫手:
正常情況下:
按先後順序是, A先更新成1000,然後B再拿1000-200,更新成800,這樣B就沒異議了。或者實在要2個同時更新,那也只能有一個成功,這樣也沒異議。二、MP來實現樂觀鎖
樂觀鎖的實現,通過增加一個欄位,比如version,來記錄每次的更新。
查詢數據的時候帶出version的值,執行更新的時候,會再去比較version,如果不一致,就更新失敗。
還是用之前的user表,增加了新的欄位
version
。
1.在實體類裡增加對於的欄位,並且加上自動填充(你也可以每次手動填充)
2. 配置插件
為了便於管理,可以見一個包,用於存放各種配置類,順便把配置在啟動類裡的mapper掃描也換到這裡來。
3.測試樂觀鎖
先新增一條測試數據:
新增成功,可以看到version值是0。
再來試一下正常的修改:
修改成功,可以看到version 變成了1。
最後,模擬下並發更新,樂觀鎖更新失敗的情況:
按照樂觀鎖的原理,user2是可以更新成功的,也就是name會修改為「大周4」,version會加1。user因為前後拿到的版本號不對,更新失敗。
結果符合預期,我們也可以看下mybatis的日誌,進一步了解一下:
可以看到上面首先是2個查詢,查詢到的version都是1。
接著,第一個執行update語句的時候,where條件中version=1,可以找到數據,於是更新成功,切更新version=2。
而第二個再執行update的時候,where條件version=1,已經找不到了,因為version已經被上面的更新成了2,所以更新失敗。