目录
看此文前先看 MySQL 中的锁机制,本文是看了该文后做的一些实验记录和总结,仅够参考,更多的是需要自己在mysql中做实验,理解。
总结
1、通过主键索引操作单个记录,只会对该主键索引记录加record lock;因此,此时只是不能修改该条记录。
2、通过唯一索引操作单个记录,会对改唯一索引记录加record lock,对该唯一索引对应的 主键索引记录加record lock;因此,此时只是不能修改该条记录
3、通过普通索引操作单个记录,会对该范围进行左延伸/右延伸 后,延伸后得到一个新范围;
- 对新范围里的普通索引记录,加Next-key锁(record lock+gap lock);
- 对该新范围里的主键索引记录加record lock;
因此,此时不能修改该条记录,也不能在新范围里插入新(普通索引)纪录
4、通过主键索引操作范围多个记录,会对该范围进行左延伸/右延伸,延伸后得到一个新范围;
- 对新范围里的主键索引记录,加Next-key锁(record lock+gap lock)
因此,此时不能修改范围里的记录,也不能在新范围里插入新(主键索引)纪录
5、通过唯一索引操作范围多个记录,会对该范围进行左延伸/右延伸,延伸后得到一个新范围;
- 对新范围里的唯一索引记录,加Next-key锁(record lock+gap lock)
- 对新范围里的主键索引记录加record lock;
因此,此时不能修改范围里的记录,也不能在新范围里插入新(唯一索引)纪录
6、通过普通索引操作范围多个记录,同 5,因此,此时不能修改范围里的记录,也不能在新范围里插入新(普通索引)纪录。
7、通过无索引操作单个记录或范围多个记录,会对所有的主键索引记录加即Next-key锁(record lock+gap lock)。因此,此时无法修改任何值、插入任何行。
这也就是为什么 MySQL 推荐通过索引操作数据,最好是主键。
实验前的准备
mysql> create table t(
id int primary key,
c1 int,
c2 int,
c3 int
);
mysql> create unique index idx_t_c1 on t(c1);
mysql> create index idx_t_c2 on t(c2);
mysql> insert into t(id,c1,c2,c3) values (1,1,2,3),(5,4,5,6),(9,7,8,9),(13,10,11,12);
mysql> select * from t;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 1 | 1 | 2 | 3 |
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
| 13 | 10 | 11 | 12 |
+----+------+------+------+
4 rows in set (0.00 sec)
mysql> desc t;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| c1 | int(11) | YES | UNI | NULL | |
| c2 | int(11) | YES | MUL | NULL | |
| c3 | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
4 rows in set (0.01 sec)
mysql> SET GLOBAL innodb_status_output=ON;
mysql> SET GLOBAL innodb_status_output_locks=ON;
对于select的语句中有范围
如果select的范围为 [a,b], 则会向左延伸,找第一个<=a的值;向右延伸,找第一个值>b的值,形成新的范围[a,d]或(c,d],锁住这之间的间隙;
举例:假设当前表中一个索引为id, 该索引的值有 1 5 9 13 ,其实就是前面创建的那张表。
select * from t;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 1 | 1 | 2 | 3 |
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
| 13 | 10 | 11 | 12 |
+----+------+------+------+
select * from t where id between 5 and 9 for update; 锁定[5],(5,9],(9,13], 即[5,13]
select * from t where id between 3 and 9 for update; 锁定(1,5],(5,9],(9,13], 即(1,13]
select * from t where id between 3 and 13 for update; 锁定(1,5],(5,9],(9,13],(13,+∞) 即(1,+∞)
select * from t where id between 3 and 15 for update; 锁定(1,5],(5,9],(9,13],(13,+∞) 即(1,+∞)
select * from t where id between 1 and 9 for update; 锁定[1],(1,5],(5,9],(9,13] 即[1,13]
select * from t where id between 1 and 13 for update; 锁定[1],(1,5],(5,9],(9,13],(13,+∞) 即[1,+∞]
select * from t where id between 1 and 15 for update; 锁定[1],(1,5],(5,9],(9,13],(13,+∞) 即[1,+∞]
select * from t where id between 0 and 15 for update; 锁定(-∞,1],(1,5],(5,9],(9,13],(13,+∞) 即[-∞,+∞]
这个是为后面做准备的,看不懂没关系,先往下看,看到(4)的时候可以回来看这里。
(1)通过主键索引操作单个记录
mysql> begin;
mysql> SELECT * FROM t WHERE id = 5 FOR UPDATE;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
+----+------+------+------+
1 row in set (0.00 sec)
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43298, ACTIVE 11 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 30, OS thread handle 11440, query id 429 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43298 lock mode IX
RECORD LOCKS space id 317 page no 3 n bits 72 index PRIMARY of table `test`.`t` trx id 43298 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152011d; asc R ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
结果分析:存在 2 个锁结构(表IX锁、主键索引上的X记录锁),锁定了1条索引记录;
- 表t上存在IX锁;
- 主键索引上存在一个X记录锁,锁定了id为5的那条主键索引记录(主键索引记录包括该行全部数据)。
(2)通过唯一索引操作单个记录
- c1是唯一索引
mysql> begin;
mysql> SELECT * FROM t WHERE c1 = 4 FOR UPDATE;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
+----+------+------+------+
1 row in set (0.00 sec)
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43299, ACTIVE 10 sec
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 30, OS thread handle 11440, query id 433 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43299 lock mode IX
RECORD LOCKS space id 317 page no 4 n bits 72 index idx_t_c1 of table `test`.`t` trx id 43299 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000004; asc ;;
1: len 4; hex 80000005; asc ;;
RECORD LOCKS space id 317 page no 3 n bits 72 index PRIMARY of table `test`.`t` trx id 43299 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152011d; asc R ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
结果分析:存在 3 个锁结构(表IX锁、主键索引上X记录锁、唯一索引上的X记录锁),锁定了2条索引记录;
- 表t上存在IX锁;
- 主键索引上存在一个X记录锁,锁定了id为5的主键索引记录(主键索引记录包括该行全部数据);
- 索引idx_t_c1上存在一个X记录锁,锁定了c1为4的唯一索引记录(唯一索引记录中只包括id和c1两列)。
噢!它是先通过唯一索引idx_t_c1,由c1=4找到id=5,知道了id=5,再通过主键索引找到id=5的记录。
记录锁永远都是锁定索引记录,锁定非聚簇索引时会先锁定聚簇索引。
如果表中没有定义索引,InnoDB 默认为表创建一个隐藏的聚簇索引,并且使用该索引锁定记录。
(3)通过普通索引操作单个记录
- c2为普通索引
mysql> begin;
mysql> SELECT * FROM t WHERE c2 = 5 FOR UPDATE;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
+----+------+------+------+
1 row in set (0.00 sec)
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43480, ACTIVE 15 sec
4 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 53, OS thread handle 11440, query id 656 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43480 lock mode IX
RECORD LOCKS space id 318 page no 5 n bits 80 index idx_t_c2 of table `test`.`t` trx id 43480 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 4; hex 80000005; asc ;;
RECORD LOCKS space id 318 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 43480 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a9c8; asc ;;
2: len 7; hex c600000142011d; asc B ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
RECORD LOCKS space id 318 page no 5 n bits 80 index idx_t_c2 of table `test`.`t` trx id 43480 lock_mode X locks gap before rec
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000008; asc ;;
1: len 4; hex 80000009; asc ;;
结果分析:存在4个锁结构(表IX锁、主键索引上X记录锁、普通索引上的X next-key锁、普通索引上的X间隙锁),锁定了3个索引记录;
- 表 t 上存在 IX 锁,
- 索引 idx_t_c2 上存在一个 next-key 锁(锁定了c2 (2, 5])
索引 idx_t_c2 还有一个 X 间隙锁(c2 (5,8] 被锁定 ); - 主键索引上存在一个 X 记录锁(id = 5的主键索引记录被锁定)。
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 1 | 1 | 2 | 3 |
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
| 13 | 10 | 11 | 12 |
+----+------+------+------+
--另一个事务
mysql> begin;
mysql> insert into t(id,c1,c2,c3) values (-1,-1,1,0); 成功
mysql> insert into t(id,c1,c2,c3) values (-7,-7,2,0); 成功
mysql> insert into t(id,c1,c2,c3) values (-3,-3,3,0); 失败
mysql> insert into t(id,c1,c2,c3) values (-4,-4,4,0); 失败
mysql> insert into t(id,c1,c2,c3) values (-5,-5,5,0); 失败
mysql> insert into t(id,c1,c2,c3) values (-6,-6,7,0); 失败
mysql> insert into t(id,c1,c2,c3) values (-2,-2,8,0); 失败
mysql> insert into t(id,c1,c2,c3) values (7,-7,12,0); 成功
mysql> rollback;
反正就是c2的值在(2,5],(5,8]之间的,都不能插入,对于c2为5的记录不能更新。
(4)通过主键索引操作范围多个记录
mysql> begin;
mysql> SELECT * FROM t WHERE id BETWEEN 3 and 10 FOR UPDATE;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
+----+------+------+------+
2 rows in set (0.00 sec)
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43300, ACTIVE 9 sec
2 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 30, OS thread handle 11440, query id 437 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43300 lock mode IX
RECORD LOCKS space id 317 page no 3 n bits 72 index PRIMARY of table `test`.`t` trx id 43300 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152011d; asc R ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000009; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152012a; asc R *;;
3: len 4; hex 80000007; asc ;;
4: len 4; hex 80000008; asc ;;
5: len 4; hex 80000009; asc ;;
Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 8000000d; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d5000001520137; asc R 7;;
3: len 4; hex 8000000a; asc ;;
4: len 4; hex 8000000b; asc ;;
5: len 4; hex 8000000c; asc ;;
结果分析:存在2个锁结构(表IX锁、主键索引上X next-key锁),锁定了3条记录;
- 表t上存在IX锁;
- 主键索引上存在3个X Next-key锁( (1, 5]、(5, 9]、(9,13]被锁定 );
(此时,只可以插入id小于1和大于13的数据;)
-- 另一个事务
mysql> begin;
mysql> insert into t(id,c1,c2,c3) values (0,0,0,0); 成功
mysql> update t set c1 = 111 where id = 1; 成功
mysql> insert into t(id,c1,c2,c3) values (2,222,222,222); 失败
mysql> insert into t(id,c1,c2,c3) values (3,333,333,333); 失败
mysql> insert into t(id,c1,c2,c3) values (4,444,444,444); 失败
mysql> update t set c1 = 555 where id = 5; 失败
mysql> insert into t(id,c1,c2,c3) values (8,888,888,888); 失败
mysql> insert into t(id,c1,c2,c3) values (10,100,100,100); 失败
mysql> insert into t(id,c1,c2,c3) values (11,1111,1111,1111); 失败
mysql> insert into t(id,c1,c2,c3) values (12,1222,1222,1222); 失败
mysql> update t set c1 = 1333 where id = 13; 失败
mysql> insert into t(id,c1,c2,c3) values (14,1444,1444,1444); 成功
mysql> rollback;
再举一个例子:
mysql> begin;
mysql> SELECT * FROM t WHERE id BETWEEN 3 and 15 FOR UPDATE;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
| 13 | 10 | 11 | 12 |
+----+------+------+------+
3 rows in set (0.00 sec)
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43336, ACTIVE 11 sec
2 lock struct(s), heap size 1136, 4 row lock(s)
MySQL thread id 49, OS thread handle 19320, query id 495 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43336 lock mode IX
RECORD LOCKS space id 317 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 43336 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152011d; asc R ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000009; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152012a; asc R *;;
3: len 4; hex 80000007; asc ;;
4: len 4; hex 80000008; asc ;;
5: len 4; hex 80000009; asc ;;
Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 8000000d; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d5000001520137; asc R 7;;
3: len 4; hex 8000000a; asc ;;
4: len 4; hex 8000000b; asc ;;
5: len 4; hex 8000000c; asc ;;
- 主键索引上存在4个X Next-key锁(id (1,5]、(5, 9]、(9,13]、(13, +无穷)被锁定);
-- 另一个事务
mysql> begin;
mysql> insert into t(id,c1,c2,c3) values (0,0,0,0); 成功
mysql> update t set c1 = 111 where id = 1; 成功
mysql> insert into t(id,c1,c2,c3) values (2,222,222,222); 失败
mysql> insert into t(id,c1,c2,c3) values (22,2222,2222,2222); 失败
mysql> rollback;
(5)通过唯一索引操作范围多个记录
mysql> begin;
mysql> SELECT * FROM t WHERE c1 BETWEEN 3 and 9 FOR UPDATE;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
+----+------+------+------+
2 rows in set (0.00 sec)
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43378, ACTIVE 11 sec
3 lock struct(s), heap size 1136, 5 row lock(s)
MySQL thread id 53, OS thread handle 11440, query id 554 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43378 lock mode IX
RECORD LOCKS space id 317 page no 4 n bits 80 index idx_t_c1 of table `test`.`t` trx id 43378 lock_mode X
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000004; asc ;;
1: len 4; hex 80000005; asc ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000007; asc ;;
1: len 4; hex 80000009; asc ;;
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 8000000a; asc ;;
1: len 4; hex 8000000d; asc ;;
RECORD LOCKS space id 317 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 43378 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152011d; asc R ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000009; asc ;;
1: len 6; hex 00000000a91d; asc ;;
2: len 7; hex d500000152012a; asc R *;;
3: len 4; hex 80000007; asc ;;
4: len 4; hex 80000008; asc ;;
5: len 4; hex 80000009; asc ;;
结果分析:存在3个锁结构,锁定了5条索引记录;
- 表t上存在IX锁;
- 主键索引上存在2个X 记录锁(id为5、9的主键索引记录被锁定,即c2为4和7的记录不能被修改了)
- 唯一索引上存在3个X Next-key锁(c1 (1, 4]、(4, 7]、(7, 10]被锁定),
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 1 | 1 | 2 | 3 |
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
| 13 | 10 | 11 | 12 |
+----+------+------+------+
--另一个事务
mysql> begin;
mysql> insert into t(id,c1,c2,c3) values (-1,0,0,0); 成功
mysql> insert into t(id,c1,c2,c3) values (0,3,0,0); 失败
mysql> insert into t(id,c1,c2,c3) values (6,-1,0,0); 成功
mysql> insert into t(id,c1,c2,c3) values (8,22,0,0); 成功
mysql> insert into t(id,c1,c2,c3) values (22,8,0,0); 失败
mysql> update t set c2 = 0 where c1 = 1; 成功
mysql> update t set c2 = 0 where c1 = 4; 失败
mysql> update t set c2 = 0 where c1 = 2; 成功,因为锁的是间隙,不是索引记录(因为并没有这条记录,所以实际上什么都没有改变)
mysql> update t set c2 = 0 where c1 = 10; 失败,不能通过c1改变c1=10这条记录的其他值
mysql> update t set c2 = 0 where id = 13; 成功,但是可以通过id改变c1=10的这条记录
mysql> rollback;
反正就是c1的值在(1,4],(4,7],(7,10]之间的,都不能插入,对于c1为4、7的记录不能更新。
c1的10的记录可以更新,但是不能通过c1改变c1=10这条记录的其他值,可以通过id改变c1=10的这条记录
(6)通过普通索引操作范围多个记录
同(5)
(7)通过无索引操作单个记录或范围多个记录
mysql> begin;
mysql> SELECT * FROM t WHERE c3 = 6 FOR UPDATE; (SELECT * FROM t WHERE c3 BETWEEN 4 and 10 FOR UPDATE;)
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 5 | 4 | 5 | 6 |
+----+------+------+------+
mysql> SHOW ENGINE INNODB STATUS;
---TRANSACTION 43513, ACTIVE 14 sec
2 lock struct(s), heap size 1136, 5 row lock(s)
MySQL thread id 53, OS thread handle 11440, query id 730 localhost ::1 root starting
SHOW ENGINE INNODB STATUS
TABLE LOCK table `test`.`t` trx id 43513 lock mode IX
RECORD LOCKS space id 318 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 43513 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 00000000a9c8; asc ;;
2: len 7; hex c6000001420110; asc B ;;
3: len 4; hex 80000001; asc ;;
4: len 4; hex 80000002; asc ;;
5: len 4; hex 80000003; asc ;;
Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000005; asc ;;
1: len 6; hex 00000000a9c8; asc ;;
2: len 7; hex c600000142011d; asc B ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000005; asc ;;
5: len 4; hex 80000006; asc ;;
Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 80000009; asc ;;
1: len 6; hex 00000000a9c8; asc ;;
2: len 7; hex c600000142012a; asc B *;;
3: len 4; hex 80000007; asc ;;
4: len 4; hex 80000008; asc ;;
5: len 4; hex 80000009; asc ;;
Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
0: len 4; hex 8000000d; asc ;;
1: len 6; hex 00000000a9c8; asc ;;
2: len 7; hex c6000001420137; asc B 7;;
3: len 4; hex 8000000a; asc ;;
4: len 4; hex 8000000b; asc ;;
5: len 4; hex 8000000c; asc ;;
结果分析:存在2个锁结构,锁定了5个索引项;
- 表t上存在IX锁,
- 主键索引上存在5个next-key锁,锁定了所有的主键范围((-∞,1], (1,5],(5,9],(9,13],(13,+∞)。
此时其他事务无法插入任何数据。 不仅无法修改c3=6的那条记录,其他所有记录都无法修改。
--另一个事务
mysql> begin;
mysql> insert into t(id,c1,c2,c3) values (-1,-1,1,0); 失败
mysql> insert into t(id,c1,c2,c3) values (20,-2,2,0); 失败
mysql> update t set c2 = 0 where c3 = 6; 失败
mysql> update t set c2 = 0 where c3 = 9; 失败
mysql> update t set c2 = 0 where id = 9; 失败
mysql> rollback;
也就是说这个时候,不能够执行任何写操作。
死锁
mysql> select * from t;
+----+------+------+------+
| id | c1 | c2 | c3 |
+----+------+------+------+
| 1 | 1 | 2 | 3 |
| 5 | 4 | 5 | 6 |
| 9 | 7 | 8 | 9 |
| 13 | 10 | 11 | 12 |
+----+------+------+------+
4 rows in set (0.00 sec)
--事务T1 --事务T2
mysql> begin; mysql> begin;
mysql> select * from t where id=22 for update;
Empty set (0.00 sec)
mysql> select * from t where id=23 for update;
Empty set (0.00 sec)
mysql> insert into t(id,c1,c2,c3) values (23,-2,2,0);
mysql> insert into t(id,c1,c2,c3) values (22,-1,1,0);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Query OK, 1 row affected (10.77 sec)
因为事务T1获得了(13,+∞)的间隙锁,事务T2也获得(13,+无穷大)的间隙锁(因为最大ID是13,锁13之后的间隙,间隙锁之间不互斥)。T2想要插入id=23的记录,但是间隙被T1锁了;T1想要插入id=22的记录,但是间隙被T2锁了;就造成了死锁。
从上面实验可以看出,mysql检测到死锁后,就把是T1给关闭了,然后T2才能成功插入数据。