MySQL Transaction--RC事务隔离级别下加锁测试
==============================================================================
非索引列更新
在读提交的事务隔离级别下,在非索引列上进行更新和删除会对修改行的主键索引上加行锁。 如果更新或删除条件为c3=4且c3列上没有索引则: 1、阻止其他会话删除表中任意行数据 2、允许其他会话插入任意记录,包括允许插入c3=4的记录 3、阻止其他会话更新c3=4的记录(主键上有X排他锁) 4、允许其他会话更新c3<>4的记录,包括允许将c3<>4的记录更新为c3=4的记录。 在读提交事务隔离级别下进行更新和删除操作,存储引擎层会对扫描到的记录加X排他锁,但MySQL Server层对此进行优化,将不满足条件记录上的锁进行释放。
##=========================================## 测试数据: CREATE TABLE `tb4001` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `c1` int(11) DEFAULT NULL, `c2` varchar(200) DEFAULT NULL, `c3` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_c1` (`c1`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; insert into tb4001(c1,c2,c3) values(2,2,2); insert into tb4001(c1,c2,c3) values(4,4,4); insert into tb4001(c1,c2,c3) values(7,7,7); insert into tb4001(c1,c2,c3) values(8,8,8); ##=========================================## ##测试1:在没有索引的列上更新 ##事务隔离级别:RC 会话1: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=777 where c3=7; ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; insert into tb4001(c1,c2,c3) values(9,9,9); 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; insert into tb4001(c1,c2,c3) values(9,9,7); 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=888 where c3=8; 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c3=7 where c3=8; 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=888 where c3=8; ##执行结果:会话2被阻塞 使用SHOW ENGINE INNODB STATUS \G查看阻塞发生时的锁信息 ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 75 page no 3 n bits 80 index PRIMARY of table `test1`.`tb4001` trx id 10596 lock_mode X locks rec but not gap waiting Record lock, heap no 8 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000003; asc ;; 1: len 6; hex 00000000294f; asc )O;; 2: len 7; hex 26000000391afe; asc & 9 ;; 3: len 4; hex 80000007; asc ;; 4: len 3; hex 373737; asc 777;; 5: len 4; hex 80000007; asc ;; ------------------ ---TRANSACTION 10575, ACTIVE 1702 sec 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 52, OS thread handle 140674621650688, query id 1245 127.0.0.1 admin ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; delete from tb4001 where c3=8; ##执行结果:会话2被阻塞 使用SHOW ENGINE INNODB STATUS \G查看阻塞发生时的锁信息 ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 75 page no 3 n bits 80 index PRIMARY of table `test1`.`tb4001` trx id 10580 lock_mode X locks rec but not gap waiting Record lock, heap no 8 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000003; asc ;; 1: len 6; hex 00000000294f; asc )O;; 2: len 7; hex 26000000391afe; asc & 9 ;; 3: len 4; hex 80000007; asc ;; 4: len 3; hex 373737; asc 777;; 5: len 4; hex 80000007; asc ;; ------------------ ---TRANSACTION 10575, ACTIVE 232 sec 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 52, OS thread handle 140674621650688, query id 1245 127.0.0.1 admin ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; delete from tb4001 where c3=2; ##执行结果:会话2被阻塞 使用SHOW ENGINE INNODB STATUS \G查看阻塞发生时的锁信息 ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 75 page no 3 n bits 80 index PRIMARY of table `test1`.`tb4001` trx id 10589 lock_mode X locks rec but not gap waiting Record lock, heap no 8 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000003; asc ;; 1: len 6; hex 00000000294f; asc )O;; 2: len 7; hex 26000000391afe; asc & 9 ;; 3: len 4; hex 80000007; asc ;; 4: len 3; hex 373737; asc 777;; 5: len 4; hex 80000007; asc ;; ------------------ ---TRANSACTION 10575, ACTIVE 493 sec 2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1 MySQL thread id 52, OS thread handle 140674621650688, query id 1245 127.0.0.1 admin ##=========================================##
==============================================================================
非唯一索引更新
在读提交的事务隔离级别下,按照非主键非唯一索引进行更新和删除,会对满足条件的行加行锁 如果更新或删除条件为c1=4且c1列上有非唯一索引则: 1、阻止其他会话修改删除c1=4的记录 2、允许其他会话插入任意记录,且允许插入c1=4的记录 3、允许其他会话更新c1<>4的任意记录,且允许将记录修改为c1=4的记录 4、允许其他会话删除c1<>4的任意记录
##=========================================## 测试数据: CREATE TABLE `tb4001` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `c1` int(11) DEFAULT NULL, `c2` varchar(200) DEFAULT NULL, `c3` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_c1` (`c1`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; insert into tb4001(c1,c2,c3) values(2,2,2); insert into tb4001(c1,c2,c3) values(4,4,4); insert into tb4001(c1,c2,c3) values(7,7,7); insert into tb4001(c1,c2,c3) values(8,8,8); ##=========================================## ##测试1:在没有索引的列上更新 ##事务隔离级别:RC 会话1: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=777 where c1=7; ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; insert into tb4001(c1,c2,c3) values(9,9,9); 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; insert into tb4001(c1,c2,c3) values(7,7,7); 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; delete from tb4001 where c1=8; 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=888 where c1=8; 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c1=7 where c1=8; 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; delete from tb4001 where c1=4; 会话2未被阻塞成功执行 ##=========================================##
==============================================================================
非唯一索引+条件更新
在读提交事务隔离级别下,按照对非唯一索引列进行更新,会对扫描到的所有索引记录进行更新,无论该记录是否满足WHERE中的其他条件
##=========================================## 测试数据: CREATE TABLE `tb4001` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `c1` int(11) DEFAULT NULL, `c2` varchar(200) DEFAULT NULL, `c3` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_c1` (`c1`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; insert into tb4001(c1,c2,c3) values(2,2,2); insert into tb4001(c1,c2,c3) values(4,4,4); insert into tb4001(c1,c2,c3) values(4,4,44); insert into tb4001(c1,c2,c3) values(7,7,7); insert into tb4001(c1,c2,c3) values(8,8,8); ##=========================================## ##会话1: ##事务隔离级别:RC 会话1: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=444 where c1=4 and c3=4; ##=========================================## ##会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=444 where c1=4 and c3=44; 会话2未被阻塞成功执行 ##执行结果:会话2被阻塞 ---TRANSACTION 10784, ACTIVE 13 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s) MySQL thread id 76, OS thread handle 140674621384448, query id 1662 127.0.0.1 admin updating update tb4001 set c2=444 where c1=4 and c3=44 ------- TRX HAS BEEN WAITING 13 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 79 page no 4 n bits 72 index idx_c1 of table `test1`.`tb4001` trx id 10784 lock_mode X locks rec but not gap waiting Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000004; asc ;; 1: len 8; hex 8000000000000002; asc ;; ------------------ ---TRANSACTION 10783, ACTIVE 21 sec 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 MySQL thread id 75, OS thread handle 140674621916928, query id 1658 127.0.0.1 admin ##=========================================##
==============================================================================
按照非唯一组合索引更新
假设表TB1上有列C1和C2,有索引IDC_C1_C2(C1,C2) 在读提交事务隔离级别下,会话1按照C1=3 AND C2=4进行更新,则: 1、会话2按照C1=3 AND C2=4进行更新,更新操作被阻塞 2、会话2按照C1=3 AND C2=M(M<>4)进行更新,更新操作不会被阻塞
##=========================================## ##测试数据 CREATE TABLE `tb4001` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `c1` int(11) DEFAULT NULL, `c2` varchar(200) DEFAULT NULL, `c3` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_c1_c3` (`c1`,`c3`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; insert into tb4001(c1,c2,c3) values(2,2,2); insert into tb4001(c1,c2,c3) values(4,4,4); insert into tb4001(c1,c2,c3) values(4,4,44); insert into tb4001(c1,c2,c3) values(7,7,7); insert into tb4001(c1,c2,c3) values(8,8,8); ##=========================================## ##会话1: ##事务隔离级别:RC 会话1: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=444 where c1=4 and c3=4; ##=========================================## ##会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=444 where c1=4 and c3=44; 会话2未被阻塞成功执行 ##=========================================##
==============================================================================
按照唯一索引更新
在读提交的事务隔离级别下,在唯一索引列上进行更新和删除在唯一索引的索引行上加排他锁。 如果更新或删除条件为c1=7且c1列上存在唯一索引则: 1、阻止其他会话删除和修改c1=7的记录 2、阻止其他会话插入c1=7的记录 3、允许其他会话插入\删除\修改c1<>7的记录,但不允许将记录修改为c1=7的记录 以上限制通过在唯一索引的索引记录上加排他锁X来实现,不会生产GAP锁
##=========================================## 测试数据: CREATE TABLE `tb4001` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `c1` int(11) DEFAULT NULL, `c2` varchar(200) DEFAULT NULL, `c3` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_c1` (`c1`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; insert into tb4001(c1,c2,c3) values(2,2,2); insert into tb4001(c1,c2,c3) values(4,4,4); insert into tb4001(c1,c2,c3) values(7,7,7); insert into tb4001(c1,c2,c3) values(8,8,8); ##=========================================## ##测试1:在没有索引的列上更新 ##事务隔离级别:RC 会话1: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=777 where c1=7; ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; insert into tb4001(c1,c2,c3) values(6,6,6); 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; insert into tb4001(c1,c2,c3) values(7,7,7); ##执行结果:会话2被阻塞 使用SHOW ENGINE INNODB STATUS \G查看阻塞发生时的锁信息 mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap size 1136, 3 row lock(s) MySQL thread id 53, OS thread handle 140674620851968, query id 1331 127.0.0.1 admin updating delete from tb4001 where c3=8 ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 75 page no 3 n bits 80 index PRIMARY of table `test1`.`tb4001` trx id 10606 lock_mode X waiting Record lock, heap no 11 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000003; asc ;; 1: len 6; hex 000000002966; asc )f;; 2: len 7; hex 34000000270908; asc 4 ' ;; 3: len 4; hex 80000007; asc ;; 4: len 3; hex 373737; asc 777;; 5: len 4; hex 80000007; asc ;; ------------------ ---TRANSACTION 10598, ACTIVE 567 sec 4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1 MySQL thread id 52, OS thread handle 140674621650688, query id 1306 127.0.0.1 admin ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c2=888 where c1=8; 会话2未被阻塞成功执行 ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; update tb4001 set c1=7 where c1=8; 会话2未被阻塞成功执行 ##执行结果:会话2被阻塞 使用SHOW ENGINE INNODB STATUS \G查看阻塞发生时的锁信息 ---TRANSACTION 10686, ACTIVE 14 sec updating or deleting mysql tables in use 1, locked 1 LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1 MySQL thread id 53, OS thread handle 140674620851968, query id 1480 127.0.0.1 admin updating update tb4001 set c1=7 where c1=8 ------- TRX HAS BEEN WAITING 7 SEC FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 76 page no 4 n bits 72 index idx_c1 of table `test1`.`tb4001` trx id 10686 lock mode S waiting Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000007; asc ;; 1: len 8; hex 8000000000000003; asc ;; ------------------ ---TRANSACTION 10675, ACTIVE 265 sec 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 MySQL thread id 52, OS thread handle 140674621650688, query id 1450 127.0.0.1 admin ##=========================================## 会话2: SET SESSION tx_isolation='READ-COMMITTED'; START TRANSACTION; SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; delete from tb4001 where c1=8; 会话2未被阻塞成功执行 ##=========================================##