玩命加载中 . . .

22-锁


22.1.1 写-写情况

当一个事务想对这条记录做改动时,首先会看看内存中有没有与这条记录关联的锁结构,当没有的时候就会在内存中生成一个锁结构与之关联

22.1.2 读-写或写-读情况

  • 方案1:读操作利用多版本并发控制(MVCC),写操作进行加锁

查询语句只能读到在生成 ReadView 之前已提交事务所做的更改,在生成 ReadView 之前未提交的事务或者之后才开启的事务所做的更改是看不到的

  • 方案2:读、写操作都采用加锁的方式

采用 MVCC 方式的话, 读-写操作彼此并不冲突,性能更高,采用加锁方式的话,读-写操作彼此需要排队执行,影响性能

22.1.3 一致性读

事务利用 MVCC 进行的读取操作称之为一致性读,一致性读并不会对表中的任何记录做加锁操作

22.1.4 锁定读

在读取记录前就为该记录加锁的读取方式称为锁定读

  • 共享锁:Shared Locks,简称 S 锁。在事务要读取一条记录时,需要先获取该记录的 S 锁
  • 独占锁:Exclusive Locks,也称排他锁,简称 X 锁。在事务要改动一条记录时,需要先获取该记录的 X 锁
兼容性 X锁 S锁
X锁 × ×
S锁 ×
  • 对读取的记录加 S 锁
SELECT ... LOCK IN SHARE MODE;

如果当前事务执行了该语句,那么它会为读取到的记录加 S 锁,这样允许别的事务继续获取这些记录的 S 锁,但是不能获取这些记录的 X 锁。如果别的事务想要获取这些记录的 X 锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的 S 锁释放掉

  • 对读取的记录加 X 锁
SELECT ... FOR UPDATE;

为读取到的记录加 X 锁,这样既不允许别的事务获取这些记录的 S 锁,也不允许获取这些记录的 X 锁,如果别的事务想要获取这些记录的 S 锁或者 X 锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的 X 锁释放掉

22.2 多粒度锁

  • 意向共享锁:Intention Shared Lock,简称 IS 锁,当事务准备在某条记录上加 S 锁时,需要先在表级别加一个 IS 锁
  • 意向独占锁:Intention Exclusive Lock,简称 IX 锁,当事务准备在某条记录上加 X 锁时,需要先在表级别加一个 IX 锁

IS、IX锁是表级锁,它们的提出仅仅为了在之后加表级别的 S 锁和 X 锁时可以快速判断表中的记录是否被上锁,以避免用遍历的方式来查看表中有没有上锁的记录,也就是说其实 IS 锁和 IX 锁是兼容的,IX 锁和 IX 锁是兼容的

兼容性 X IX S IS
X × × × ×
IX × ×
S × ×
IS ×
  • Record Lock:在记录上加的锁

  • Gap Lock:为了防止插入幻影记录而提出的

  • Next-Key Lock:本质就是一个记录锁和一个 gap 锁的合体,它既能保护该条记录,又能阻止别的事务将新记录插入被保护记录前边的间隙

22.4 语句加锁分析

22.4.1 普通的SELECT语句

  • READ UNCOMMITTED隔离级别下,不加锁,直接读取记录的最新版本;可能出现脏读、不可重复读、幻读
  • READ COMMITTED隔离级别下,不加锁,在每次执行普通的SELECT语句时会生成一个 ReadView,这样避免了脏读现象,但没有避免不可重复读和幻读
  • REPEATABLE READ隔离级别下,不加锁,只在第一次执行普通的SELECT语句时会生成一个 ReadView,这样就避免了脏读和不可重复读,也能在一定程度上避免幻读

之所以不能完全避免幻读,是因为 ReadView 并不能阻止当前事务执行UPDATE或者DELETE语句来改动其他事务新插入的记录,这样新纪录的trx_id就变成了当前事务的id,后面再使用普通的SELECT就能看到这条记录了,可以认为 InnoDB 中的 MVCC 并不能完全禁止幻读

22.4.2 锁定读语句

  • 匹配模式

如果被扫描的区间是一个单点扫描区间,就可以说匹配模式为精确匹配

  • 唯一性搜索

如果在扫描某个扫描区间的记录前,就能事先确定该扫描区间内最多只包含一条记录,就把这种情况称作唯一性搜索

需要满足这些条件:

  • 匹配模式为精确匹配
  • 使用的索引是主键或唯一二级索引
  • 如果使用的索引是唯一二级索引,搜索条件不能为“索引 IS NULL”
  • 如果索引包含多个列,每一列都要用到

事务在执行过程中所获取的锁一般在事务提交或者回滚时才会释放,但是在隔离级别不大于READ COMMITTED时,在某些情况下也会提前将一些不符合搜索条件的记录上的锁释放掉

对于锁定读的语句,在隔离级别不大于READ COMMITTED时,会为当前记录加记录锁;在隔离级别不小于REPEATABLE READ时,会为当前记录加next-key

  • 隔离级别不大于READ COMMITTED,且读取聚簇索引记录

对读取到的记录加 S 型记录锁,如果该记录不满足其他条件则释放锁,如果满足条件则发送到客户端,但不释放锁

  • 隔离级别不小于REPEATABLE READ,且读取聚簇索引记录

对读取到的记录加 S 型 next-key 锁,如果该记录不满足其他条件也不会释放锁,如果满足条件则发送到客户端,但不释放锁

SELECT ... FOR UPDATE语句的加锁过程与SELECT ... LOCK IN SHARE MODE语句类似,只不过为记录加的是 X 锁

  • 当隔离级别不大于READ COMMITTED时,如果匹配模式为精确匹配,则不会为扫描区间后面的下一条记录加锁
  • 当隔离级别不小于REPEATABLE READ时,如果匹配模式为精确匹配,则会为扫描区间后面的下一条记录加 gap 锁

22.4.3 半一致性读的语句

半一致性读(Semi-Consistent Read)是一种夹在一致性读和锁定读之间的读取方式。当隔离级别不大于READ COMMITTED且执行UPDATE语句时将使用半一致性读。所谓半一致性读,就是当UPDATE语句读取到已经被其他事务加了 X 锁的记录时,InnoDB 会将该记录的最新提交版本读出来,然后判断该版本是否与UPDATE语句中的搜索条件相匹配。如果不匹配,则不对该记录加锁,从而跳到一条记录;如果匹配,则再次读取该记录并对其进行加锁。这样处理只是为了让UPDATE语句尽量少被别的语句阻塞

22.6 死锁

InnoDB 死锁检测机制:当检测到死锁发生时,会选择一个较小的事务进行回滚(是指在事务执行过程中插入、更新或删除的记录条数较少的事务)


文章作者: kunpeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kunpeng !
  目录