基于MySQL 8.0和Innodb存储引擎,总结update语句在不同情况下产生的锁,包括MDL锁、意向锁、行锁和间隙锁等。
测试环境
MySQL的Update语句会产生什么锁,却决于很多因素。本文基于MySQL 8.0、Innodb存储引擎、可重复读隔离级别,具体版本和建表语句如下:
1 | mysql> select version(); |
MDL读锁
MDL是指Metadata Locking,中文叫元数据锁,它是一个表级锁,用来保护表结构、触发器等并发访问的一致性。UPDATE
语句产生的mdl读锁会阻塞表结构修改语句,例如:
事务1执行UPDATE
语句:
1 | mysql> begin; |
事务2增加索引将被阻塞:
1 | mysql> alter table t1 add index index_age(`age`); |
意向写锁
意向锁也是一个表级锁,执行UPDATE
语句将会产生一个意向写锁。意向写锁只会阻塞LOCK TABLES xxx WRITE
语句,例如:
事务1执行UPDATE
语句:
1 | mysql> begin; |
事务2获取表级写锁将被阻塞:
1 | mysql> lock tables t1 write; |
行级锁
行级锁包括:
- 记录锁 Record Lock
- 间隙锁 Gap Lock
- Next-Key Lock (记录锁和间隙锁的组合)
行级锁怎么加,会受到索引访问方式的影响,下面通过例子说明。
例1: 通过主键更新1条记录
1 | update t1 set age=222 where id=5; |
这是一条最常见的UPDATE
语句,where
条件使用主键索引来更新数据,加锁分为2种情况:
id=5
的记录存在,只会在id=5
的主键索引加上一个行锁。id=5
的记录不存在,会在主键索引上加上一个间隙锁。
间隙锁会阻止其它事务往这个间隙中插入数据。事务1执行UPDATE
不存在的记录:
1 | mysql> begin; |
表格中id
列只有1、5、6,执行update ... where id=2
将产生一个间隙锁(1, 5)
。例如插入id=3
的记录将被阻塞:
1 | mysql> insert into t1 (id,age) values (3, 80); |
例2: 通过主键更新一个范围
1 | mysql> begin; |
当where
条件使用主键的范围来更新时,会同时加上行锁和间隙锁。例如表格中id
列只有1、5、6,上述UPDATE
语句将会:
- 给
id=5
加上行锁 - 给
id=6
加上行锁 - 加上间隙锁
(6, +∞)
插入(id=8)
的记录将被间隙锁阻塞:
1 | mysql> insert into t1 (id,age) values (8, 80); |
例3:通过无索引的字段更新
1 | mysql> begin; |
上面例子中,使用where age=200
条件,其中age
字段没有索引,上述UPDATE
语句将会锁住表格中的每一条记录和每个间隙,其它UPDATE
、INSERT
和DELETE
语句都无法执行:
1 | mysql> insert into t1 (id,age) values (8, 80); /* insert被阻塞 */ |
例4:通过唯一索引更新
表格中有2个辅助索引c
和d
,其中d
是唯一索引。where
条件使用唯一索引更新记录:
1 | mysql> begin; |
当d=80
记录存在时,将会:
- 给唯一索引
d=80
加上行锁(没有间隙锁) d=80
对应记录的主键索引也会加上行锁(没有间隙锁)- 没有用到的辅助索引
c
不受影响
当d=80
的记录不存在时,将会在唯一索引d
上加上间隙锁(与例1类似)。
使用唯一索引进行范围更新时:
1 | mysql> update t1 set age=111 where d>70; |
与例2类似,将会:
- 给匹配到的主键索引记录加上行锁
- 给匹配到的唯一辅助索引记录加上行锁
- 给辅助索引加上间隙锁
例5:通过(非唯一)二级索引更新
1 | mysql> update t1 set age=111 where c=300; |
表格中c
字段有非唯一辅助索引,以上SQL将:
- 在辅助索引上加上Next-key锁
- 给
where
条件匹配到的记录加上行锁
例6:多个条件更新
where
中包含多个条件时,加什么锁取决于使用了哪个索引,可以使用explain
查看:
1 | mysql> explain update t1 set age=222 where age=111 and c=100 and d=200; |
上面例子中where
条件中同时包含唯一索引d
、非唯一索引c
、无索引的字段age
,从explain
的结果中可以看出,最终使用了索引d
,则加锁情况参考例4:通过唯一索引更新。
update语句加锁总结
update语句可能加的锁包括:
- MDL锁
- 意向锁
- 行锁
- 间隙锁
- Next-key Locking