1. 悲觀鎖簡介
悲觀鎖(Pessimistic Concurrency Control,縮寫PCC),它指的是對數據被外界修改持保守態度,因此,在整個數據處理過程中, 將數據處於鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制,也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據。
2. 悲觀鎖舉例(MySQL InnoDB為例)
商品表t_goods中有字段status標識商品的狀態,0標識未售出,1標識已售出。當用戶生產訂單時,該商品的status必須為0則認為是有效訂單。
1 create table t_goods (id int(11) primary key auto_increment, name varchar(255), status int(1) default 0); 2 insert into t_goods (name) value ('手機'); 3 select * from t_goods; 4 5 create table t_orders (id int(11) primary key auto_increment, goods_id int(11));
假如不添加鎖,則順序為:
# 1.查詢商品 select * from t_goods where id = 1 and status = 0; # 2.生產訂單 insert into t_orders (goods_id) value (1); # 3.更新狀態 update t_goods set status = 1 where id = 1; select * from t_goods;
這種情況在單線程或只有一個用戶使用時沒有問題,但是在多線程或並發場景下是不安全的。
若在更新前,有其他訂單提前將狀態更新為1,則商品被重復下單,造成數據不一致的情況。
使用悲觀鎖實現,則順序為:
1 #必須關閉Mysql數據庫的自動提交屬性 2 #因為Mysql默認使用autocommit模式,即執行更新操作后,Mysql會立刻提交結果 3 4 show variables like '%autocommit%'; 5 #臨時設置 6 set autocommit = 0; 7 8 # 1.開啟事務(begin; begin work; start transaction;) 9 begin; 10 # 2.查詢商品 11 select * from t_goods where id = 1 and status = 0 for update; 12 # 3.生產訂單 13 insert into t_orders (goods_id) value (1); 14 # 4.更新狀態 15 update t_goods set status = 1 where id = 1; 16 # 5.提交事務(commit; commit work;都可以) 17 commit; 18 19 select * from t_goods;
使用select * from tbale_name for update實現了悲觀鎖,即對id=1的數據進行加鎖,其他事務對該數據的操作會進行阻塞,當前事務提交后,才允許其他事務對其進行操作。
注意:當開始一個事務,執行select * from tbale_name for update,在另外事務中select ...for update時會阻塞,但是使用select * from tbale_name 則不會阻塞。
select ... for update詳解:Mysql的InnoDB引擎默認使用Row-Level Lock(行級鎖),因此在查詢中要指定主鍵,否則,Mysql會執行Table-Level Lock(表級鎖)。
1 #分別在兩個窗口執行 2 # 1. 指定主鍵,並且主鍵存在,row lock 3 select * from t_goods where id = 1 for update; 4 # 2. 指定主鍵,主鍵不存在,no lock 5 select * from t_goods where id = 999 for update; 6 # 3. 無主鍵,table lock 7 select * from t_goods where name = '電腦' for update; 8 # 4. 主鍵不明確,table lock 9 select * from t_goods where id > 10 for update; 10 select * from t_goods where id != 1 for update; 11 # 5. 指定索引,並且索引存在,row lock 12 select * from t_goods where status = 1 for update; 13 # 6. 指定索引,索引不存在,no lock 14 select * from t_goods where status = 999 for update;
3. 樂觀鎖簡介
樂觀鎖(Optimistic Locking,簡寫PL),相對悲觀鎖而言,樂觀鎖認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會對數據的沖突與否進行檢測,如果發現沖突了,則讓返回錯誤信息,讓用戶處理。
4. 樂觀鎖舉例
使用數據版本(Version)記錄機制實現。這是樂觀鎖最常用的一種實現方式。即為數據增加一個版本標識,即增加一個數字類型的 “version” 字段。當讀取數據時,將version字段的值一同讀出,數據每更新一次,對version值加一。當提交更新時,判斷數據庫表對應記錄的當前version值與取出來的version值進行比對,如果數據庫表當前version值與取出來的version值相等,則執行更新,否則不更新。
Mybatis Plus中提供了完整的樂觀鎖機制:https://mp.baomidou.com/guide/optimistic-locker-plugin.html#主要適用場景