全局锁

执行全局锁的命令:

1
flush tables with read lock

执行命令以后,整个表都会处于只读阶段。只读阶段阻塞了如下操作:

  • 对于数据的增删改(insert, update, delete)
  • 对于表结构的更改(drop,alter)

主要应用场景为全局逻辑备份

但是因为inno DB中有运用read view实现可重复读,修改后由于事务,数据库数据的一致性可以得到保证,所以造成无法改动的全局锁由于细粒度过大基本上被废弃。

但是,对于 MyISAM 这种不支持事务的引擎,在备份数据库时就要使用全局锁的方法。

表级锁

表锁

1
2
3
4
5
6
7
8
//表级别的共享锁,也就是读锁;
lock tables t_student read;

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

//释放当前会话的所有表锁
unlock tables

元数据锁(MDL)

元数据锁的目的是为了防止线程对表数据进行修改的时候,对于表格式进行变更,这个锁的对象是表的数据结构。

它分为读锁和写锁两种锁:

  • 当对于一张表做CRUD操作的时候,他新增的是MDL读锁
  • 当对于一张表做出表结构修改的时候,他新增的是MDL写锁

MDL锁的机制是读读共享,读写互斥,写写互斥。

申请MDL锁的事务会生成一个队列,写锁的优先级比读锁的要高。也就是说,当一张表加上了MDL读锁,之后有一个事务想要对表结构做出修改,加上MDL写锁,那么他之后的对于想要加入CRUD操作的读锁都是位于阻塞的状态。

意向锁

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

原因是如果没有表的锁,需要一行一行判断有无加上行级锁,效率低下,加上意向锁可以达到剪枝的效果,排除完全没有被加上行级锁的表。

  • 当用InnoDB存储引擎的时候,当对于一条记录加上了共享锁,那么会对于其表加上意向共享锁。
  • 当用InnoDB存储引擎的时候,当对于一条记录加上了独占锁,那么会对于其表加上意向独占锁。

共享锁和独占锁是行级锁,意向独占锁和意向共享锁是表级锁。两者互不冲突。

AUTO-INC锁

在插入一条数据的时候,可以不输入主键就实现主键的自增,主要通过的就是auto-inc锁。

AUTO-INC 锁是特殊的表锁机制,锁不是再一个事务提交后才释放,而是再执行完插入语句后就会立即释放

但是这种情况在大量插入的情况下,插入语句就变成了并行操作,容易导致执行效率的低下。

因此, 在 MySQL 5.1.22 版本开始,InnoDB 存储引擎提供了一种轻量级的锁来实现自增。

一样也是在插入数据的时候,会为被 AUTO_INCREMENT 修饰的字段加上轻量级锁,然后给该字段赋值一个自增的值,就把这个轻量级锁释放了,而不需要等待整个插入语句执行完后才释放锁

这个时候减少了锁存在的时间,由新增语句完成后才释放锁提前到主键赋值完成就释放锁,减少了锁存在的时间,提高了锁的性能。

行级锁

1
2
3
4
5
//对读取的记录加共享锁
select ... lock in share mode;

//对读取的记录加独占锁
select ... for update;

共享锁(S)满足读读共享,读写互斥,也就是可以被多个线程所持有。

独占锁(X)满足读写互斥,写写互斥,只能被一个线程所持有。

Record Lock

记录锁加锁的对象是一条数据。当对于一个事务加上了记录锁的时候,其他事务就不能对其进行修改。但是记录锁也有共享锁和独占锁的区别:

  • 当一个事务对一条记录加上了S(共享锁)的时候,其他事务仍然可以对其加上S锁,但不能对其加上X锁;
  • 当一个事务对一条记录加上了X(独占锁)的时候,其他事务不能对加上S锁,也不能加上X锁。

当提交时,所有的锁都会被释放。

Gap Lock

Gap Lock被称为间隙锁,只存在于可重复度级别,主要用于解决可重复度中的幻读问题。

比如表中有一个(2,5)的间隙锁,那么id = 4的这条记录就无法再次被插入了。

间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系。

Next-Key Lock

Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。

next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的

行级锁不同情况下是如何加锁的

唯一索引等值查询

  • 当查询的记录是「存在」的,在索引树上定位到这一条记录后,将该记录的索引中的 next-key lock 会退化成「记录锁」

img

  • 当查询的记录是「不存在」的,在索引树找到第一条大于该查询记录的记录后,将该记录的索引中的 next-key lock 会退化成「间隙锁」

唯一索引非等值查询

当唯一索引进行范围查询时,会对每一个扫描到的索引加 next-key 锁,然后如果遇到下面这些情况,会退化成记录锁或者间隙锁

  • 情况一:针对「大于等于」的范围查询,因为存在等值查询的条件,那么如果等值查询的记录是存在于表中,那么该记录的索引中的 next-key 锁会退化成记录锁

  • 情况二:针对「小于或者小于等于」的范围查询,要看条件值的记录是否存在于表中:

    • 当条件值的记录不在表中,那么不管是「小于」还是「小于等于」条件的范围查询,扫描到终止范围查询的记录时,该记录的索引的 next-key 锁会退化成间隙锁,其他扫描到的记录,都是在这些记录的索引上加 next-key 锁。
    • 当条件值的记录在表中,如果是「小于」条件的范围查询,扫描到终止范围查询的记录时,该记录的索引的 next-key 锁会退化成间隙锁,其他扫描到的记录,都是在这些记录的索引上加 next-key 锁;如果「小于等于」条件的范围查询,扫描到终止范围查询的记录时,该记录的索引 next-key 锁不会退化成间隙锁。其他扫描到的记录,都是在这些记录的索引上加 next-key 锁。

非唯一索引等值查询

  • 当查询的记录「存在」时,由于不是唯一索引,所以肯定存在索引值相同的记录,于是非唯一索引等值查询的过程是一个扫描的过程,直到扫描到第一个不符合条件的二级索引记录就停止扫描,然后在扫描的过程中,对扫描到的二级索引记录加的是 next-key 锁,而对于第一个不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁。同时,在符合查询条件的记录的主键索引上加记录锁
  • 当查询的记录「不存在」时,扫描到第一条不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁。因为不存在满足查询条件的记录,所以不会对主键索引加锁

非唯一索引非等值查询

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

非索引查询

如果锁定读查询语句,没有使用索引列作为查询条件,或者查询语句没有走索引查询,导致扫描是全表扫描。那么,每一条记录的索引上都会加 next-key 锁,这样就相当于锁住的全表,这时如果其他事务对该表进行增、删、改操作的时候,都会被阻塞

sql_safe_updates 设置为1,开启安全更新模式。

update 语句必须满足如下条件之一才能执行成功:

  • 使用 where,并且 where 条件中必须有索引列;
  • 使用 limit;
  • 同时使用 where 和 limit,此时 where 条件中可以没有索引列;