MySQL的MVCC是什么,有什么用?
一、介绍
面试被问到了MVCC
,我不知道啊,一脸懵逼!
于是回家查询了资料,记录一下
实际上,MVCC
的全称是Multi Version ConCurrency Control
,翻译过来就是多版本并发控制。
二、概念
1)隔离级别
指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行。在MySQL
中,这样大幅度提高了InnoDB
的并发度。在内部实现中,InnoDB
通过undo log
保存每条数据的多个版本,并且能够找回数据历史版本提供给用户读,每个事务读到的数据版本可能是不一样的。在同一个事务中,用户只能看到该事务创建快照之前已经提交的修改和该事务本身做的修改。
首先我们先了解一下数据库事务的隔离级别
- 未提交读(READ UNCOMMITED):也就是脏读,当一个事务读取到另外一个事务修改但未提交的数据时
- 已提交读 (READ COMMITED):简称
RC
- 可重复读(REPEATABLE READ):简称
RR
- 可串行化(SERIALIZABLE)
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | 可能 | 可能 | 可能 |
已提交读 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
可串行化 | 不可能 | 不可能 | 不可能 |
那么先介绍下面的现象
- 脏读:当一个事务读取到另外一个事务修改但未提交的数据时
- 不可重复读:在同一个事务中,同样的条件
SQL
查询出的结果不一致 - 幻读:在同一个事务中,同样的条件
SQL
查询出的结果不一致(重点在于,数据有新增或者删除,导致的结果不一致)
2)undo log
在上面有提到undo log
,那么这个undo log
是什么呢?
实际上是当多个事务操作一条数据时,每个事务中的每个操作都会产生一条记录,比如说下面这样
可以看到,当一个事务TX111
修改了数据值,就会产生一条undo log
,并记录指向上一条最原始的这条undo log
。
如果有多条事务,修改同一条数据,那么就会产生链表一样的结构,我们称为undo log
版本链表,如下
undo log
,会进行删除,但不是立即删除。 它会在确保,当前undo log
不被引用后,再进行删除。也就是当事务所有完成后,也就是commit
、rollback
,保留最终确认下的undo log
,并删除之前所有的版本链。
3)快照读、当前读
好的,现在要介绍一下快照读和当前读,只要介绍了这个,我们就能了解MVCC
到底是什么了
- 快照读(
readView
):当执行查询select
语句时,提取数据的一个记录 - 当前读:当执行下面的语句时,提取数据的一个记录
insert
、update
、delete
、select...for update
、select...lock in share mode
上面了解到他们是一个数据记录,那么其中他们有什么数据呢
字段 | 说明 |
---|---|
m_ids | 当前活跃的事务编号集合,也就是还没有被提交回滚的事务集合 |
min_trx_id | 最小的活跃事务编号 |
max_trx_id | 预分配事务编号,当前最大事务编号 1 |
creator_trx_id | 快照读创建者的事务编号 |
好的,我们来进行理解一下这个快照读
4)多版本并发控制流程
上面的概念都看完了,接下来可以讲讲MVCC
了,他是怎么使用RR
、RC
来影响事务读取的数据的呢?
快照读配合当前读会影响,读取的结果,我们看下面的undo log
和readView
我们要确定版本时,就是拿着快照读去匹配版本链上的每一个undo log
,从最后往前进行判断
使用这些判断条件,MySQL
就能确定要读取的版本了
- 判断
undo log
的trx_id == creator_trx_id
- 相等,则说明这条
undo log
修改,就是本事务自己更新修改的。可以访问
- 相等,则说明这条
- 判断
undo log
的trx_id < min_trx_id
- 成立,则说明当前判断的这个
undo log
已经提交。可以访问
- 成立,则说明当前判断的这个
- 判断
undo log
的trx_id >= max_trx_id
- 成立,则说明当前判断的这个
undo log
,是在产生快照读之后创建的事务。所以不允许访问
- 成立,则说明当前判断的这个
- 判断
min_trx_id <= trx_id < max_trx_id
,成立则继续判断,trx_id
是否存在于m_ids
里面- 成立,则说明
undo log
的事务还没有提交。不允许访问 - 不成立,则说明
undo log
的事务已经提交。允许访问
- 成立,则说明
根据上面的判断条件,我们来进行判断
- 首先是第一个
undo log
trx_id == creator_trx_id
,即TX222 == TX333
。不成立,继续下一个判断条件trx_id < min_trx_id
,即TX222 < TX222
。不成立,则继续下一个判断条件trx_id >= max_trx_id
,即TX222 >= TX334
。不成立,则继续向下判断min_trx_id <= trx_id < max_trx_id
,即TX222 <= TX222 <= TX334
。成立,则还要判断是否处于活跃事务集合中TX222
处于集合[TX222, TX333]
之中,不允许访问
- 上面的条件都不满足后,我们将要继续下一个
undolog
trx_id == creator_trx_id
,即TX111 == TX333
。不成立,继续下一个判断条件trx_id < min_trx_id
,即TX111 < TX222
。- 成立,则说明
TX111
的事务已经提交,允许访问,确定下来一个数据的访问
- 成立,则说明
按照上面的方法,进行判断
会读取到这样的一个结果,注意看,在TX333
的事务下,三次读取出现了三次不同的结果,这便是不可重复读。
那么MVCC
该如何控制呢,其实问题主要的发生原因,是每次的readView
不一致导致的,既然快照读不一致,所查询的结果肯定也不一致。
那么只需要,将同一个事务中快照读,变成同一份,不就是可重复读的隔离级别了嘛。
故事务中,将第一次快照读进行保存,后面的读取都按照这份快照读进行复用。
那么为什么说可重复读RR
,并不能完全解决幻读的问题呢?
因为,在同一个事务中,快照读是复用的,一旦事务中出现了一次当前读,也就是执行了update
等语句,那么就会重新刷新快照读。一旦快照读发生了改变,幻读就有可能出现了。
不可重复读,是指一个事务中,两次读取的结果不一致的现象。 但同一个事务中,如果是因为自己修改了数据,从而导致两次查询结果不一致的情况,这是正常现象,不叫不可重复读 这也正是,为什么发生当前读后,快照读要重新进行生成的原因。因为要读取到自己事务上一刻修改的数据。
三、最后
好了,我是被面试问到的,真的是一脸懵逼,服了...
我是半月,你我一同共勉!!!