1、mysql悲觀鎖:在整個數據處理過程中,將數據處於鎖定狀態。悲觀鎖的實現,依靠數據庫提供的鎖機制,每次會申請鎖並加鎖和解鎖操作
第一步:兩個終端均關閉自動提交
左邊:
右邊:
第二步:左邊利用 select .... for update 的悲觀鎖語法鎖住記錄
select * from employee where id = 1 for update;
第三步:右邊也嘗試利用 select .... for update 的悲觀鎖語法鎖住記錄
可以看到,Sql語句被掛起(被阻塞)!
提示:如果被阻塞的時間太長,會提示如下:
第四步:左邊執行更新操作並提交事務
Sql語句:
update employee set money = 0 + 1 where id = 1;
commit;
結果:
第五步:查看右邊Sql語句的變化
2、mysql樂觀鎖:樂觀鎖認爲一般情況下數據不會造成衝突,所以在數據進行提交更新時纔會對數據的衝突與否進行檢測。如果沒有衝突那就OK;如果出現衝突了,則返回錯誤信息並讓用戶決定如何去做。
數據庫本身不提供支持,而是需要開發者自己來實現,通過版本號控制及時間戳控制,重試操作也需要開發自己實現
版本號控制的原理:
- 爲表中加一個 version 字段;
- 當讀取數據時,連同這個 version 字段一起讀出;
- 數據每更新一次就將此值加一;
- 當提交更新時,判斷數據庫表中對應記錄的當前版本號是否與之前取出來的版本號一致,如果一致則可以直接更新,如果不一致則表示是過期數據需要重試或者做其它操作(PS:這完完全全就是 CAS 的實現邏輯呀~)
mysql> select * from t_goods;
+----+--------+------+---------+
| id | status | name | version |
+----+--------+------+---------+
| 1 | 1 | 道具 | 1 |
| 2 | 2 | 裝備 | 2 |
+----+--------+------+---------+
2 rows in set
update t_goods
set status=2, version = version + 1
where id=#{id} and version = #{version};
總結&對比
悲觀鎖 | 樂觀鎖 | |
概念 | 查詢時直接鎖住記錄使得其它事務不能查詢,更不能更新 | 提交更新時檢查版本或者時間戳是否符合 |
語法 | select ... for update | 使用 version 或者 timestamp 進行比較 |
實現者 | 數據庫本身 | 開發者 |
適用場景 | 併發量大 | 併發量小 |
類比Java | Synchronized關鍵字 | CAS 算法 |
悲觀鎖
優點:寫多讀少的併發環境中使用
缺點:加鎖會增加系統開銷,雖然能保證數據的安全,但數據處理吞吐量低,不適合在讀書寫少的場合下使用
樂觀鎖
優點:在讀多寫少的併發場景下,可以避免數據庫加鎖的開銷
缺點:在寫多讀少的併發場景下,即在寫操作競爭激烈的情況下,會導致CAS多次重試,衝突頻率過高,導致開銷比悲觀鎖更高
參考地址:https://www.cnblogs.com/cyhbyw/p/8869855.html
https://blog.csdn.net/SnailMann/article/details/88388829