MySQL锁的分类

数据库有很多客户端访问,访问过程会出现同时处理同一张表、同一条数据的情况,处理的逻辑各不一致,为了保证数据最后的准确性,需要加锁,实现对资源的控制。数据库是这样,Java代码中的并发编程也是如此。

MySQL锁的分类

  • 全局锁
  • 表级别锁:表锁、元数据锁(MDL)、表意向锁、AUTO-INC 锁
  • 行级别锁:Record Lock(记录锁)、Gap Lock(间隙锁)、Next-Key Lock(临键锁)、插入意向锁

全局锁

要使用全局锁,则要执行这条命令:

flush tables with read lock

执行后,整个数据库就处于只读状态了。应用:做全库逻辑备份。

表级别锁

表锁

//表级别的共享锁,也就是读锁;
lock tables t_student read;

//表级别的独占锁,也就是写锁;
lock tables t_stuent write;
unlock tables

元数据锁(MDL)

meta data lock MDL 是在事务提交后才会释放,这意味着事务执行期间,MDL 是一直持有的。

表意向锁

有了「意向锁」,由于在对记录加独占锁前,先会加上表级别的意向独占锁,那么在加「独占表锁」时,直接查该表是否有意向独占锁,如果有就意味着表里已经有记录被加了独占锁,这样就不用去遍历表里的记录。 所以,意向锁的目的是为了快速判断表里是否有记录被加锁。

AUTO-INC 锁

  • 表里的主键通常都会设置成自增的,这是通过对主键字段声明 AUTO_INCREMENT 属性实现的。
  • 可以在插入数据时,可以不指定主键的值,数据库会自动给主键赋值递增的值,这主要是通过 AUTO-INC 锁实现的。
  • AUTO-INC 锁是特殊的表锁机制,锁不是再一个事务提交后才释放,而是再执行完插入语句后就会立即释放。

在插入数据时,会加一个表级别的 AUTO-INC 锁,然后为被 AUTO_INCREMENT 修饰的字段赋值递增的值,等插入语句执行完成后,才会把 AUTO-INC 锁释放掉。 一个事务在持有 AUTO-INC 锁的过程中,其他事务的如果要向该表插入语句都会被阻塞,从而保证插入数据时,被 AUTO_INCREMENT 修饰的字段的值是连续递增的。

优化

但是, AUTO-INC 锁再对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞。因此, 在 MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级的锁来实现自增。 一样也是在插入数据的时候,会为被 AUTO_INCREMENT 修饰的字段加上轻量级锁,然后给该字段赋值一个自增的值,就把这个轻量级锁释放了,而不需要等待整个插入语句执行完后才释放锁。

行级别锁

Record Lock(记录锁)

仅仅把一条记录锁上

Gap Lock(间隙锁)

  • 锁定一个范围,但是不包含记录本身
  • 只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。
  • 表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。
  • 隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的

Next-Key Lock(临键锁)

  • Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
  • 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的。

插入意向锁

  • 如果有的话,插入操作就会发生阻塞,直到拥有间隙锁的那个事务提交为止(释放间隙锁的时刻),在此期间会生成一个插入意向锁,表明有事务想在某个区间插入新记录,但是现在处于等待状态。
  • 插入意向锁名字虽然有意向锁,但是它并不是意向锁,它是一种特殊的间隙锁,属于行级别锁。
  • 间隙锁锁住的是一个区间,那么「插入意向锁」锁住的就是一个点。因而从这个角度来说,插入意向锁确实是一种特殊的间隙锁。