参考:
[隔离]zhuanlan.zhihu.com/p/367820387
[锁]zhuanlan.zhihu.com/p/187345419
四大隔离级别
四大隔离级别,都会存在哪些并发问题呢
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | √ | √ | √ |
读已提交 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |
重复读与幻读区别:重复读针对的是update数据值,幻读针对的是insert、delete行
MVCC原理
MVCC(Multi-Version Concurrency Control)是InnoDB引擎独有的,数据库隔离级别读已提交RC、可重复读RR 都是基于MVCC实现的,相对于加锁简单粗暴的方式,它用更好的方式去处理读写冲突,能有效提高数据库并发性能。
查询一条记录,基于MVCC,是怎样的流程
- 获取事务自己的版本号,即事务ID
- 获取Read View(RC事务内每次查询都生成一个独立的Read View;RR事务内只在第一次查询时生成ReadView)(ReadView不是)
- 查询数据库得到的数据,然后与Read View中的事务版本号进行比较。
- 如果不符合Read View的可见性规则, 即就需要Undo log中历史快照;
- 最后返回符合规则的数据
InnoDB 实现MVCC,是通过 Read View+ Undo Log
实现的,Undo Log 保存了历史快照,Read View可见性规则帮助判断当前版本的数据是否可见。
Read View是如何保证可见性判断的呢?我们先看看Read view 的几个重要属性
- m_ids:当前系统中那些活跃(未提交)的读写事务ID, 它数据结构为一个List。
- min_limit_id:表示在生成ReadView时,当前系统中活跃的读写事务中最小的事务id,即m_ids中的最小值。
- max_limit_id:表示生成ReadView时,系统中应该分配给下一个事务的id值。
- creator_trx_id: 创建当前read view的事务ID
Read view 匹配条件规则如下:
- 如果数据事务ID
trx_id < min_limit_id
,表明生成该版本的事务在生成Read View前,已经提交(因为事务ID是递增的),所以该版本可以被当前事务访问。 - 如果
trx_id>= max_limit_id
,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。(这里有个漏洞,如果大于max的事务,先完成了提交,本事务再提交修改可能会覆盖大于max的结果。) - 如果
min_limit_id =<trx_id< max_limit_id
,需腰分3种情况讨论
- (1).如果
m_ids
包含trx_id
,则代表Read View生成时刻,这个事务还未提交,但是如果数据的trx_id
等于creator_trx_id
的话,表明数据是自己生成的,因此是可见的。- (2)如果
m_ids
包含trx_id
,并且trx_id
不等于creator_trx_id
,则Read View生成时,事务未提交,并且不是自己生产的,所以当前事务也是看不见的;- (3).如果
m_ids
不包含trx_id
,则说明你这个事务在Read View生成之前就已经提交了,修改的结果,当前事务是能看见的。
锁
在RC和RR事务内,如果sql语句里没有指定加什么锁(FOR UPDATE排他锁 or LOCK IN SHARE MODE共享锁):
- select工作在MVCC模式下,通常都是实现 非阻塞读,
- update、delete操作只锁定必要的行。
锁粒度
行级锁
Mysql的行锁是通过锁索引实现的,如果加锁查询的时候没有使用过索引,会将整个聚簇索引都锁住,相当于锁表了。
- 记录锁:record Lock 是指聚簇索引中真实存在的数据
- 间隙锁:gap Lock 指的是两个记录之间逻辑上尚未填入数据的部分
- 临键锁:next-key lock 指的是间隙加上它右边的记录组成的左开右闭区间
锁级表
通常发生在DDL语句\DML不走索引的语句中
锁与事务关系
mysql默认行锁类型就是临键锁(Next-Key Locks)
。当使用唯一性索引,等值查询匹配到一条记录的时候,临键锁(Next-Key Locks)会退化成记录锁;没有匹配到任何记录的时候,退化成间隙锁。
间隙锁(Gap Locks)
和临键锁(Next-Key Locks)
都是用来解决幻读问题的,在已提交读(READ COMMITTED)
隔离级别下,间隙锁(Gap Locks)
和临键锁(Next-Key Locks)
(手动加锁)都会失效!