MySQL LOCK--按照非聚集索引更新时如何加锁

测试环境

  • MySQL版本: 5.7.30
  • 事务级别: READ-COMMITTED

测试数据

mysql> show create table tb1001 G
*************************** 1. row ***************************
       Table: tb1001
Create Table: CREATE TABLE `tb1001` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c1` int(11) NOT NULL,
  `c2` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_c1` (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=524273 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> select * from tb1001 limit 10;
+----+----+----+
| id | c1 | c2 |
+----+----+----+
|  1 |  1 |  1 |
|  2 |  2 |  2 |
|  3 |  3 |  3 |
| 10 | 10 | 10 |
| 11 | 11 | 11 |
| 12 | 12 | 12 |
| 13 | 13 | 13 |
| 14 | 14 | 14 |
| 15 | 15 | 15 |
| 16 | 16 | 16 |
+----+----+----+
10 rows in set (0.00 sec)

测试准备

会话1:按照聚集索引锁定id=3的数据

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from tb1001 where id=2 for update;
+----+----+----+
| id | c1 | c2 |
+----+----+----+
|  2 |  2 |  2 |
+----+----+----+
1 row in set (0.00 sec)

测试01

会话2:按照非聚集索引查找c1<10的数据

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update tb1001 set c2=0 where c1<10;

会话2被阻塞,查看锁信息:

show engine innodb status G

---TRANSACTION 6334452, ACTIVE 10 sec fetching rows
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 3162307, OS thread handle 140609456948992, query id 44172685 172.16.24.1 mysql_admin updating
update tb1001 set c2=0 where c1<10
------- TRX HAS BEEN WAITING 10 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 168 page no 4 n bits 552 index PRIMARY of table `demodb`.`tb1001` trx id 6334452 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 0000005d73ec; asc    ]s ;;
 2: len 7; hex c1000040230110; asc    @#  ;;
 3: len 4; hex 80000002; asc     ;;
 4: len 4; hex 80000002; asc     ;;

------------------
---TRANSACTION 6334441, ACTIVE 39 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 3162293, OS thread handle 140609595356928, query id 44172194 172.16.24.1 mysql_admin

--------

可以发现会话2等待id=2的主键索引(聚集索引)上的锁,同时会话2上持有3个ROW LOCK。

测试02

保持会话2的等待,执行会话3:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from tb1001 where id=1 for update;

会话3被阻塞,查看锁信息:

show engine innodb status G

---TRANSACTION 6334476, ACTIVE 10 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 3162313, OS thread handle 140610455860992, query id 44173872 172.16.24.1 mysql_admin statistics
select * from tb1001 where id=1 for update
------- TRX HAS BEEN WAITING 10 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 168 page no 4 n bits 552 index PRIMARY of table `demodb`.`tb1001` trx id 6334476 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 00000060a80b; asc    `  ;;
 2: len 7; hex 4c0000014929bb; asc L   I) ;;
 3: len 4; hex 80000001; asc     ;;
 4: len 4; hex 80000000; asc     ;;

------------------
---TRANSACTION 6334475, ACTIVE 18 sec fetching rows
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 3162307, OS thread handle 140609456948992, query id 44173766 172.16.24.1 mysql_admin updating
update tb1001 set c2=0 where c1<10
------- TRX HAS BEEN WAITING 18 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 168 page no 4 n bits 552 index PRIMARY of table `demodb`.`tb1001` trx id 6334475 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 0000005d73ec; asc    ]s ;;
 2: len 7; hex c1000040230110; asc    @#  ;;
 3: len 4; hex 80000002; asc     ;;
 4: len 4; hex 80000002; asc     ;;

------------------
---TRANSACTION 6334441, ACTIVE 112 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 3162293, OS thread handle 140609595356928, query id 44174043 172.16.24.1 mysql_admin starting
show engine innodb status
--------
 

可以发现会话3等待id=1的主键索引(聚集索引)上的锁,持有锁的对象是会话2.

测试03

保持会话2的阻塞状态,执行会话3:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select c1 from tb1001 where c1=3 lock in share mode;
+----+
| c1 |
+----+
|  3 |
+----+
1 row in set (0.00 sec)

mysql> select * from tb1001 where id=3 lock in share mode;
+----+----+----+
| id | c1 | c2 |
+----+----+----+
|  3 |  3 |  3 |
+----+----+----+
1 row in set (0.01 sec)

可以发现会话2并未持有id=3的主键索引(聚集索引)上的锁,也没有持有c1=3的非聚集索引idx_c1上的锁。

测试04

保持会话2的阻塞状态,执行会话3:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select c1 from tb1001 where c1=2 for update;

会话被阻塞,查看锁信息:


mysql> select * from information_schema.INNODB_LOCKS;
+------------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+-----------+
| lock_id          | lock_trx_id | lock_mode | lock_type | lock_table        | lock_index | lock_space | lock_page | lock_rec | lock_data |
+------------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+-----------+
| 6336196:168:37:3 | 6336196     | X         | RECORD    | `demodb`.`tb1001` | idx_c1     |        168 |        37 |        3 | 2         |
| 6336195:168:37:3 | 6336195     | X         | RECORD    | `demodb`.`tb1001` | idx_c1     |        168 |        37 |        3 | 2         |
| 6336195:168:4:3  | 6336195     | X         | RECORD    | `demodb`.`tb1001` | PRIMARY    |        168 |         4 |        3 | 2         |
| 6335370:168:4:3  | 6335370     | X         | RECORD    | `demodb`.`tb1001` | PRIMARY    |        168 |         4 |        3 | 2         |
+------------------+-------------+-----------+-----------+-------------------+------------+------------+-----------+----------+-----------+
4 rows in set, 1 warning (0.00 sec)

show engine innodb status G

---TRANSACTION 6336196, ACTIVE 6 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 3162313, OS thread handle 140610455860992, query id 44254752 172.16.24.1 mysql_admin statistics
select c1 from tb1001 where c1=2 for update
------- TRX HAS BEEN WAITING 6 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 168 page no 37 n bits 1152 index idx_c1 of table `demodb`.`tb1001` trx id 6336196 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 80000002; asc     ;;
 1: len 4; hex 80000002; asc     ;;

------------------
---TRANSACTION 6336195, ACTIVE 15 sec fetching rows
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 3162307, OS thread handle 140609456948992, query id 44254633 172.16.24.1 mysql_admin updating
update tb1001 set c2=0 where c1<10
------- TRX HAS BEEN WAITING 15 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 168 page no 4 n bits 552 index PRIMARY of table `demodb`.`tb1001` trx id 6336195 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 0000005d73ec; asc    ]s ;;
 2: len 7; hex c1000040230110; asc    @#  ;;
 3: len 4; hex 80000002; asc     ;;
 4: len 4; hex 80000002; asc     ;;

------------------
---TRANSACTION 6335370, ACTIVE 2365 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 3162293, OS thread handle 140609595356928, query id 44254874 172.16.24.1 mysql_admin starting
show engine innodb status
--------

可以发现会话3(trx_id=6336196)等待会话2(trx_id=6336195)的在idx_c1上c1=2的行锁(索引记录),会话2(trx_id=6336195)等待会话1(trx=6335370)的在PRIMARY上id=2的行锁(主键记录)。

测试结论

当使用非聚集索引列进行数据更新时,MySQL会使用非聚集索引进行查找,对于查找到满足过滤条件的每一行索引记录:

  1. 在查找到的非聚集索引记录上加锁。
  2. 根据非聚集索引记录上包含的聚集索引键值进行回表查找。
  3. 在查找到的聚集索引记录上加锁。
  4. 循环1、2、3步处理下一条满足过滤条件的数据。
原文地址:https://www.cnblogs.com/gaogao67/p/14656022.html