跳到主要内容

MySQL锁总结

全局锁

flush tables with read lockunlock tables

主要用于数据库备份,整个数据库处于只读状态

如果支持MVCC,那么可以用可重复读隔离级别进行数据库备份, mysqldump 时加上 –single-transaction 参数

表级锁

表锁

lock tables t_student read/write;unlock tables

元数据锁MDL

对表CRUD,加MDL读锁

对表做变更,加MDL写锁

事务执行期间,MDL 是一直持有的,写锁获取优先级高于读锁

意向锁

  • 加上共享锁之前,需要先在表级别加上一个意向共享锁
  • 加上独占锁之前,需要先在表级别加上一个意向独占锁

意向共享锁和意向独占锁是表级锁,不会和行级的共享锁和独占锁发生冲突,而且意向锁之间也不会发生冲突,只会和共享表锁(lock tables ... read)和独占表锁(lock tables ... write)发生冲突。

如果没有「意向锁」,那么加「独占表锁」时,就需要遍历表里所有记录,查看是否有记录存在独占锁,这样效率会很慢。

意向锁的目的是为了快速判断表里是否有记录被加锁。

AUTO-INC 锁

数据库会自动给主键赋值递增的值,这主要是通过 AUTO-INC 锁实现的。插入语句后就会立即释放。

行级锁

InnoDB 引擎是支持行级锁的

共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。

记录锁

Record Lock,记录锁,也就是仅仅把一条记录锁上;分 S 锁(读搜)和 X 锁(写锁)

间隙锁

Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。间隙锁之间是兼容的

临键锁

Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身

插入意向锁

一个事务在插入一条记录的时候,需要判断插入位置是否已被其他事务加了间隙锁(next-key lock 也包含间隙锁)。如果有的话,插入操作就会发生阻塞,直到拥有间隙锁的那个事务提交为止(释放间隙锁的时刻),在此期间会生成一个插入意向锁,表明有事务想在某个区间插入新记录,但是现在处于等待状态。

加锁规则

加锁的对象是索引,加锁的基本单位是 next-key lock,它是由记录锁和间隙锁组合而成的,next-key lock 是前开后闭区间,而间隙锁是前开后开区间。

  • 在能使用记录锁或者间隙锁就能避免幻读现象的场景下, next-key lock 就会退化成记录锁或间隙锁。

分析加锁语句:select * from performance_schema.data_locks\G;

通过 LOCK_MODE 可以确认是 next-key 锁,还是间隙锁,还是记录锁:

  • 如果 LOCK_MODE 为 X,说明是 next-key 锁;
  • 如果 LOCK_MODE 为 X, REC_NOT_GAP,说明是记录锁;
  • 如果 LOCK_MODE 为 X, GAP,说明是间隙锁;

唯一等值查询

  • 记录存在,退化为记录锁
  • 不存在,退化为间隙锁,锁范围,不包括右边界、

唯一索引查询

会对每一个扫描到的索引加 next-key 锁,然后如果遇到下面这些情况,会退化成记录锁或者间隙锁:

  • 针对大于等于,退化为记录锁
  • 针对小于等于
    • 记录不在表中,退化为间隙锁
    • 记录在表中,小于退化为间隙锁,小于等于

非唯一索引等值查询

存在两个索引,一个是主键索引,一个是非唯一索引(二级索引),所以在加锁时,同时会对这两个索引都加锁,但是对主键索引加锁的时候,只有满足查询条件的记录才会对它们的主键索引加锁。

  • 记录存在,扫描的二级索引加next-key锁,对第一个不符合条件的二级索引加间隙锁,符合条件的主键索引加记录锁
  • 不存在,第一条不符合条件的二级索引,加间隙锁,

非唯一索引范围查询

索引的 next-key lock 不会有退化为间隙锁和记录锁的情况,也就是非唯一索引进行范围查询时,对二级索引记录加锁都是加 next-key 锁。

没有加索引

每一条记录的索引上都会加 next-key 锁,这样就相当于锁住的全表