深入理解SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE
概念和區別
SELECT ... LOCK IN SHARE MODE走的是IS鎖(意向共享鎖),即在符合條件的rows上都加了共享鎖,這樣的話,其他session可以讀取這些記錄,也可以繼續新增IS鎖,但是無法修改這些記錄直到你這個加鎖的session執行完成(否則直接鎖等待超時)。
SELECT ... FOR UPDATE 走的是IX鎖(意向排它鎖),即在符合條件的rows上都加了排它鎖,其他session也就無法在這些記錄上新增任何的S鎖或X鎖。如果不存在一致性非鎖定讀的話,那麼其他session是無法讀取和修改這些記錄的,但是innodb有非鎖定讀(快照讀並不需要加鎖),for update之後並不會阻塞其他session的快照讀取操作,除了select ...lock in share mode和select ... for update這種顯示加鎖的查詢操作。
通過對比,發現for update的加鎖方式無非是比lock in share mode的方式多阻塞了select...lock in share mode的查詢方式,並不會阻塞快照讀。
應用場景
在我看來,SELECT ... LOCK IN SHARE MODE的應用場景適合於兩張表存在關係時的寫操作,拿mysql官方文件的例子來說,一個表是child表,一個是parent表,假設child表的某一列child_id對映到parent表的c_child_id列,那麼從業務角度講,此時我直接insert一條child_id=100記錄到child表是存在風險的,因為剛insert的時候可能在parent表裡刪除了這條c_child_id=100的記錄,那麼業務資料就存在不一致的風險。正確的方法是再插入時執行select * from parent where c_child_id=100 lock in share mode,鎖定了parent表的這條記錄,然後執行insert into child(child_id) values (100)就ok了。
但是如果是同一張表的應用場景,舉個例子,電商系統中計算一種商品的剩餘數量,在產生訂單之前需要確認商品數量>=1,產生訂單之後應該將商品數量減1。
1 select amount from product where product_name='XX';
2 update product set amount=amount-1 where product_name='XX';
顯然1的做法是是有問題,因為如果1查詢出amount為1,但是這時正好其他session也買了該商品併產生了訂單,那麼amount就變成了0,那麼這時第二步再執行就有問題。
那麼採用lock in share mode可行嗎,也是不合理的,因為兩個session同時鎖定該行記錄時,這時兩個session再update時必然會產生死鎖導致事務回滾。以下是操作範例(按時間順序)
session1(開啟事務)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj lock in share mode;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj |
+-----+------------+
2 rows in set (0.00 sec)
session2(開啟事務,鎖定了相同的行)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj lock in share mode;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj |
+-----+------------+
2 rows in set (0.00 sec)
session1(這時session1在update時就會引起鎖等待,等待session2)
mysql> update test_jjj set name='jjj1' where name='jjj';
session2(這時session2同樣update,引起鎖等待,等待session1,接著檢測到死鎖,回滾session2,注意
執行時間不要超過session1的鎖等待超時檢測時間,即不要超過innodb_lock_wait_timeout設定的值)
mysql> update test_jjj set name='jjj1' where name='jjj';
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
session1(此時session1執行完成)
mysql> update test_jjj set name='jjj1' where name='jjj';
Query OK, 1 row affected (29.20 sec)
Rows matched: 1 Changed: 1 Warnings: 0
備註:可以通過以下三個命令檢視事務的狀態
1:檢視當前的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;殺死事務程式id(就是上面命令的trx_mysql_thread_id列)
2:檢視當前鎖定的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
3:檢視當前等鎖的事務
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
通過該案例可知lock in share mode的方式在這個場景中不適用,我們需要使用for update的方式直接加X鎖,從而短暫地阻塞session2的select...for update操作;以下是操作範例
session1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj for update;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj1 |
+-----+------------+
2 rows in set (0.00 sec)
session2(此時session2處於鎖等待狀態,得不到結果)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_jjj for update;
session1(這時session1 update之後提交,可完成)
mysql> update test_jjj set name='jjj1' where name='jjj';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
session2(session1提交之後session2剛才的查詢結果就出來了,也就可以再次update往下執行了)
mysql> select * from test_jjj for update;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj1 |
+-----+------------+
2 rows in set (37.19 sec)
mysql> select * from test_jjj for update;
+-----+------------+
| id | name |
+-----+------------+
| 234 | asdasdy123 |
| 123 | jjj1 |
+-----+------------+
2 rows in set (37.19 sec)
mysql> update test_jjj set name='jjj1' where name='jjj';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
通過對比,lock in share mode適用於兩張表存在業務關係時的一致性要求,for update適用於操作同一張表時的一致性要求。
相關文章
- MySQL 5.7 SELECT ... LOCK IN SHARE MODE|FOR UPDATE語句說明MySql
- Select for update/lock in share mode 對事務併發性影響
- 【MySQL】select for update 的Row Lock 與Table LockMySql
- Mysql加鎖過程詳解(4)-select for update/lock in share mode 對事務併發性影響MySql
- MySQL 共享鎖 (lock in share mode),排他鎖 (for update)MySql
- select for update
- select for update nowait 與 select for update 區別AI
- oracle select for updateOracle
- 關於 select ... for update 和 for update nowaitAI
- SELECT ... FOR UPDATE SKIP LOCKED;
- select the lock block in MSSQLBloCSQL
- select for update nowaitAI
- SQL update select語句SQL
- 由select for update鎖等待問題引發的深入思考
- 深入理解python中的select模組Python
- sql查詢更新update selectSQL
- Oracle中select ... for update的用法Oracle
- 理解select、epoll
- select for update_v$lock_dml小測試(鎖型別及模式)型別模式
- MysqL_select for update鎖詳解MySql
- select for update與autocommit關係MIT
- 使用select監視update的操作
- oracle connection,select,update 工作原理Oracle
- Default Locking for INSERT, UPDATE, DELETE, and SELECT ... FOR UPDATE (351)delete
- mysql update join,insert select 語法MySql
- mysql innodb之select for update nowaitMySqlAI
- UPDATE SET = (SELECT ) 語法的總結
- Oracle中select for update ...一些區別Oracle
- SQLite語句(二):INSERT DELETE UPDATE SELECTSQLitedelete
- 【Mysql】兩條select for update引起的死鎖MySql
- java-Mybatis XML 對映器(select,insert, update 和 delete)JavaMyBatisXMLdelete
- select into from 和 insert into select 的用法和區別
- select * 和 select 所有欄位的區別
- select count(*)和select count(1)的區別
- MySQL中SELECT+UPDATE併發更新問題MySql
- update/select也可能產生buffer busy waits。AI
- DBeaver如何生成select,update,delete,insert語句delete
- IO多路複用——深入淺出理解select、poll、epoll的實現