1.关于脏读、幻读和不可重复读
脏读:一个事务A读取到了另一个事务B未提交的数据,叫做脏读
不可重复读:事务A被事务B干扰到了!在事务A范围内,两个相同的查询,读取同一条记录,却反返回了不同的结果,即不可重复读
幻读:事务A查询一个范围内的结果集,另一个并发事务B往这个范围中插入/删除了数据,并提交。然后事务A再次查询,结果查询到了不同的结果,这就是幻读。
2.事务隔离级别
在并发情况下,读未提交的事务隔离级别下,是不加锁的会存在 脏读、幻读和不可重复读的问题
3.InnoDB七种锁介绍
3.1共享/排他锁
InnoDB存储引擎实现了两种标准的行级锁:共享锁-Share(简称S锁)、排他锁(简称X锁)
- 共享锁:在事务想要读取一条记录的时候,需要先获取该记录的共享锁。
- 排他锁:在事务想要修改一条记录的时候,需要先获取该记录的排他锁。
共享锁存在的意义
- 存在若干个事务想要读取一条记录,需要先获得该记录的共享锁,是为了避免该记录被其他事务所修改。
排他锁存在的意义
- 当一个事务想要修改一条记录,需要先获得该记录的排他锁,此时其他事务无法读取该数据,直到排他锁释放。
3.2意向锁
意向锁解释:不与行级别锁冲突的表级锁。未来某个时刻,事务可能需要加共享锁或者排他锁的时候,先提前声明一个意向。是一个表级别的锁。
为什么需要意向锁?
- 假设一个事务A获取了表中一行数据的排他锁
- 此时事务B想要读取全表数据,需要对整张表加共享锁,因此需要保证表中不存在排他锁
- 问题来了
- 事务B想要判断表中是否存在排他锁,难道要全表遍历?
- 为了解决该问题,因此有了意向锁
实际运行过程
- 当一个事务A想要对表中的数据进行 读取/修改 的时候,需要向增加一个全表级别的 意向共享锁/意向排他锁
- 此时其他事务想要对全表中的数据进行修改,就会发现表中存在全表级别的 意向锁,从而不需要避免了全表找锁
3.3记录锁
记录锁是粒度最小的锁,仅仅锁住一行。
记录锁加在索引上,如果一张表中没有索引,InnoDB会隐式创建一个索引,并用该索引加记录锁。
记录锁关键词lock_mode X locks rec but not gap
3.4间隙锁(Gap Lock)
间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,最后一个索引之后的间隙,锁住的是一个区间。
lock_mode X locks gap before rec
为了解决幻读问题
间隙锁左右都是开区间当SQL语句查询范围数据的时候,会对查询的范围区间加上间隙锁。
此时,其他事务无法在加上了间隙锁的范围区间内插入新的数据,避免了幻读。间隙锁仅在 RR 隔离级别下出现
PS:在RR(可重复读)的隔离级别下,普通查询是快照读,不会发生幻读。如果使用“当前读”(即:for update)才有可能发生幻读
3.5临键锁(Next-Key Lock)
临键锁:锁住索引本身和索引之前的间隙,左开右闭区间
临键锁的加锁场景:当SQL使用非唯一索引的条件进行数据检索的时候,会给匹配到的行加上临键锁
如下表数据
id | name | sex | age |
---|---|---|---|
1 | kk | 男 | 10 |
2 | lily | 男 | 20 |
3 | LiNa | 男 | 30 |
4 | 男 | 40 | |
5 | HH | 男 | 60 |
6 | ZZ | 男 | 80 |
7 | NN | 男 | 100 |
在该表的 age 列增加普通非空唯一索引。
那么临键锁可能的区间为
- (负无穷, 10] (10, 20] (20, 30] (30, 40] (40, 60] (60, 80] (80, 100] (100, 正无穷)
临键锁的具体产生场景:
- 如果是等值查询且记录存在,即 begin; select * from xxx where age = 30 for update;
- 此时仅仅会锁住 age=30 这一行记录
- 等值查询且记录不存在,即 begin; select * from xxx where agen = 45 for update;
- 此时会锁住 [40, 60] 的间隙 若 40 <= age <= 60 则会插入失败
- 范围查询 select * from where age >= 25 and age <=35
- 首先查询 age = 25 的记录判断是否存在,不存在 临键锁 (20, 30]
- 同上 age = 35 不存在,加锁 (30, 40]
- 总加速范围 Next-Key Lock (20, 40]
加锁规律总结
唯一索引等值查询
- 查询记录存在,记录锁 Record Lock
- 不存在,间隙锁 Gap Lock
唯一索引范围查询
- 间隙锁或记录锁
非 唯一索引等值查询
- 记录存在 Next-key Lock 临键锁 和 间隙锁 Gap Lock
- 记录不存在 间隙锁 Gap Lock
非 唯一索引范围查询
- Next-Key Lock 临键锁
3.6插入意向锁
在执行插入一行记录操作之前设置的一种间隙锁。
解决的问题:
- 多个事务,在同一个索引范围区间内,执行插入操作的时候,如果插入的数据不冲突,就不会阻塞彼此。
- 假设 数据范围 [7, 100] 多个时候在范围区间内插入不同数据的时候不会阻塞
3.7自增锁
特殊的表级别锁,针对 AUTO_INCREMENT
- 如果表中新增一条数据,就会持有自增锁
- 此时其他事务向表中插入数据必须等待自增锁释放,以便保证连续的主键值