李印晓-PHP后端工程师

0%

事务-锁结构

解决并发事务的方式有两种,一致性读和锁定读。一致性读又叫快照读,就是前面所说的MVCC 点击这里查看这篇文章 。锁定读,需要对记录进行加锁处理,是本篇文章重点要讲的。

并发事务3种场景

  1. 读读。这种情况并不会产生任何不良影响。
  2. 写写。两个事物对同一行数据进行写操作,会存在 脏写,这是我们不能容忍的,需要通过加锁让两个操作串行化执行。
  3. 读写和写读。这种情况会出现脏读/不可重复读/幻读,需要采用MVCC和锁来执行。

共享锁 和 独占锁

为了实现锁机制,MySQL的设计者实现了两种锁,

  1. 共享锁(S 锁)。当需要读取记录的时候,需要先获取S锁。
  2. 独占锁(X 锁)。当需要修改记录的时候,需要先获取X锁。

S锁和S锁是兼容的,X锁和S,X锁是不兼容的。

如果事务1获取了S锁之后,事务2要读取记录,那可以正常获取S锁,但如果事务2想获取X锁,则会被阻塞,等待事务1的S锁释放了以后再激活事务T2。

如果事务1获取的是X锁,那无论事务2想获取S还是X锁,都会被阻塞。

X 锁,就是这么”独“。

读操作加锁语句

1
2
select ... lock in share mode; # 加S锁
select ... for update; # 加X锁

多粒度锁

锁分为表锁和行锁。

如果想对表加S锁,必须确认:

  1. 表上没加X锁,表内行没加X锁

如果想对表加X锁,必须确认:

  1. 表上没加X锁,表内行没加X锁
  2. 表上没加S锁,表内行没加S锁

这里有一个问题,判断表加没加S或者X锁容易,可是判断表中行加没加,可就麻烦了,该不会要遍历记录这么低效的方式吧?设计MySQL的那些聪明的脑袋们肯定不会这么搞。

IS 和 IX

意向共享锁(IS),在对表里的记录加S锁的同时,对表加IS锁。

意向独占锁(IX),在对表里的记录加X锁的同时,对表加IX锁。

上面的问题解决了:

判断表内记录有没有S锁或X锁,只要判断表上有没有IS或IX锁就好了,高效!

行级锁类型

Record Locks 记录锁

锁定记录。分S记录锁和X记录锁。

Gap Locks 间隙锁

锁定记录之前的空隙。仅仅是为了解决幻影记录而存在的。

举个例子,user表已经有如下的数据

id value
1 马爸爸
2 东哥
8 王校长

对id=8的记录加间隙锁,意味着不能再2到8之间插入新的纪录,否则就会阻塞。

Next-Key Locks

锁定记录和记录前的空隙。就是记录锁和间隙锁的结合。

Insert Intention Locks 插入意向锁

当插入的事务遭遇间隙锁,则需要让事务线程等待,并生成一个插入意向锁等待。

隐式锁

一个事务对新插入的记录不显式的加锁,另一个事务如果获取到该记录的S锁或X锁都是不希望的。但是因为插入的记录会有创建版本号,首先确定当前事务是否还在活跃事务中,如果是,就先为当前事务生成一个锁(is_waiting=false),然后自己进入等待状态(is_waiting=false)。

问题

InnoDB有行锁了,为什么还是没有放弃表锁?

  1. 行锁虽然粒度小,但锁都是放在内存里的,遇到需要加很多行锁的情况,加锁的开销就大得很,倒不如直接加表锁。
  2. 有些情况必须加表锁,例如DDL语句的执行。