1、最近遇到一個小問題,由於insert into table1 select from table2跟其他update事務造成了死鎖,於是猜想這個insert into select的加鎖順序,實驗環境如下:
(1)隔離級別:RC
(2)innodb_autoinc_lock_mode:1
(3)version: 5.6.37-log
(4)測試的兩個表結構一樣,如下:
mysql> show create table test1; +----------+---------------------------------------------+ | Table | Create Table | +----------+---------------------------------------------+ | test1 | CREATE TABLE `test1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `num` int(11) DEFAULT NULL, `name` varchar(20) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=31620222 DEFAULT CHARSET=utf8mb4
2、測試sql如下
(1)session 1 :
select * from test1 where id=9 for update;
(2)session 2:
insert IGNORE into test2 select * from test1 where id >2 and id < 15 lock in share mode;
(3)session 3:
insert IGNORE into test2 select 11,'9527','零零七','18888888007';
3、測試順序
(1)session 1開啟事務后執行sql -------> session 2開啟事務后執行 sql ------>session 3開啟事務后執行sql;
可以發現,session 2、session 3 都是會被阻塞的,以下是鎖信息:
show engine innodb status ---TRANSACTION 1742170, ACTIVE 4 sec setting auto-inc lock mysql tables in use 1, locked 1 MySQL thread id 3284, OS thread handle 0x7f20a48a5700, query id 2919013 localhost root executing insert IGNORE into test2 select 11,'9527','零零七','18888888007' ------- TRX HAS BEEN WAITING 4 SEC FOR THIS LOCK TO BE GRANTED: TABLE LOCK table `test`.`test2` trx id 1742170 lock mode AUTO-INC waiting -- session 1 需要等待這個鎖 ------------------ TABLE LOCK table `test`.`test2` trx id 1742170 lock mode IX TABLE LOCK table `test`.`test2` trx id 1742170 lock mode AUTO-INC waiting ---TRANSACTION 1742169, ACTIVE 30 sec fetching rows mysql tables in use 2, locked 2 LOCK WAIT 6 lock struct(s), heap size 1184, 13 row lock(s) MySQL thread id 3279, OS thread handle 0x7f209e356700, query id 2919010 localhost root Sending data insert IGNORE into test2 select * from test1 where id >2 and id < 15 lock in share mode ------- TRX HAS BEEN WAITING 30 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 1536 page no 4 n bits 560 index `PRIMARY` of table `test`.`test1` trx id 1742169 lock mode S locks rec but not gap waiting -- session 2需要等待這個鎖 TABLE LOCK table `test`.`test2` trx id 1742169 lock mode AUTO-INC -- session 2 持有的鎖 RECORD LOCKS space id 1536 page no 4 n bits 560 index `PRIMARY` of table `test`.`test1` trx id 1742169 lock mode S locks rec but not gap waiting ---TRANSACTION 1742168, ACTIVE 68 sec 2 lock struct(s), heap size 360, 1 row lock(s) MySQL thread id 3283, OS thread handle 0x7f209e51d700, query id 2919007 localhost root TABLE LOCK table `test`.`test1` trx id 1742168 lock mode IX RECORD LOCKS space id 1536 page no 4 n bits 560 index `PRIMARY` of table `test`.`test1` trx id 1742168 lock_mode X locks rec but not gap --session 3持有的鎖
從鎖信息可以知道,session 3對test1表的id=9這一行加上記錄鎖,session 2先對test2表加上AUTO-INC鎖,然后等待test1表的記錄鎖,當session 3想要插入一條數據的時候因為session 2加上的AUTO-INC表級鎖,故無法插入。
由此可知,insert into table1 select from table2首先需要申請table1的自增鎖(表級),然后再去申請table2的記錄鎖。好在table1的表級鎖是基於SQL的,一旦sql執行完即釋放自增鎖,而無需等待整個事務提交。這個加鎖順序很重要,也是下一篇產生死鎖的必要條件。