MVCC:多版本并发控制(Mutilversion Concurrency Control)

MVCC细讲

20210109 22:00-23:00 ~ 20210111 21:00-24:00阅读整理
【织梦师】诗号:
江湖生涯如梦如幻,织梦一生转眼成空。

1.
致谢: //mysql 官方文档 https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html //参考博客: https://blog.csdn.net/waves___/article/details/105295060 https://www.cnblogs.com/chinesern/p/7592537.html https://blog.csdn.net/qianlia/article/details/105118141 https://github.com/facebook/mysql-5.6/blob/42a5444d52f264682c7805bf8117dd884095c476/storage/innobase/include/read0read.h#L125
2. 概念:多版本并发控制(Mutilversion Concurrency Control)。指的是一种提高并发的技术,最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,InnoDB通过undo log 保存每条数据的多个版本,并且能够找回数据历史版本提供供给用户读,每个事务读到数据版本可能是不一样的。在同一个事务中,用户只能看到该事务创建快照之前已经提交的修改和该事务本身做的修改;;;MVCC在RR和RC两个隔离级别下工作,MySQL的InnoDB存储引擎默认事务隔离级别是RR,生活通过“行级锁+MVCC”一起实现的,正常读的时候不加锁,写的时候加锁。而MVCC的实现依赖:隐藏字段、Read View 、Undo log.
隐藏字段:每行数据之后都会有三个隐藏字段
① DB_TRX_ID(6字节):表示最近一次行修改(insert,update)的事务ID,至于delete操作,innoDB认为是一个update操作,不过会更新一个另外的删除位,将表示为deleted。并且非真正的删除
②DB_ROLL_PTR(7字节):回滚指针,指向当前记录行的undo log信息;undo log可能是一个链,最上边的是最近修改的原记录
③ DB_ROW_ID(6字节):随着新行插入而单调递增的ID。理解:当表没有主键或者唯一非空索引时,innoDB就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不是包含这个行ID
3. Read View结构
其实read view (读视图),跟快照、snapshot是一个概念。主要是用来做可见性判断的,里面保存了“对本事务不可见的其他的活跃事务”;
①read view中的几个关键字段:
②low_limit_id:“高水位”,目前时间内出现的最大事务的ID+1
③up_limit_id:“低水位”,最早进来的那个事务id(活跃事务列表中最小的事务id)
④ trx_ids:其他未提交的活跃事务ID列表(不包括当前事务自己和已提交的事务(正在内存中))
4. undo log
在其中存储的是老版本数据,当一个事务需要读取记录时,如果当前记录行不可见,可以顺着undo log链找到满足其可见性条件的记录行版本;
undo log 分类:
① insert undo log:事务对insert新纪录是产生undo log,只在事务回滚时需要,并且在事务提交之后就可以丢弃了
②update undo log:事务对记录进行update和delete操作时产生的undo log,不仅在事务回滚时需要,快照也需要,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被purge线程删除
purge线程:为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下旧记录的deleted_bit,并不真正将旧记录删除。
为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。purge线程自己也维护了一个read view,如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。
5. 可见性算法分析:
在innoDB中,创建一个新的事务之后,在第一个select语句执行的时候,innoDB会创建一个快照(readview),快照中会保存系统当前不应该被本事务看到的其他活跃事务的id列表(trx_ids)。当用户在这个事务中要读取记录行的时候,innoDB会将该记录行的DB_TRX_ID与该read view中的一些变量进行比较;如:
(参数定义)
当前事务A要读取某一个记录行,该记录行的DB_TRX_ID(即最新修改该行的事务ID)赋值给trx_id,此时read view 的活跃事务列表trx_ids中最早的事务ID为up_limit_id(也就是现存事务ID最小的那个),最近的事务ID+1(现存列表中事务ID最大的事务ID)为low_limit_id,这个id其实还是个未分配的事务ID;
比较分析逻辑:
1). 当前事务trx_id<列表中的up_limit_id,表示-当前记录行的修改事务已经在本事务快照之前完成提交了,所以记录行内的数据对当前事务是可见的,并且是符合规定的现存最新记录,直接返回该记录行内的数据即可
2). 当前事务trx_id>=列表中的low_limit_id,表示-当前记录行的最新修改该行的事务在当前事务快照之后才进行修改,所以该记录行的当前值对于当前事务是不可见的,所以只能从当前记录的回滚指针往前混滚,取出旧事物的最新DB_TRX_ID,将它作为trx_id,然后依次按之前的方法比较
3). 当前事务trx_id,是 up_limit_id>trx_id>low_limit_id,表示-当前记录行的最新修改事务在当前事务快照创建时,可能处于活动状态,也可能已经提交了,所以:
1. 如果trx_id在trx_ids中能找到,说明当前事务处于活跃状态列表中,那么也就是该记录行的修改内容不能被当前事务所看到,所以要使用记录行回滚指针指向旧数据回滚段,,取出旧事务的最新DB_TRX_ID,将它作为trx_id,然后依次按之前的方法比较
2. 如果trx_id不存在于活跃列表中,说明当前事务已经提交了,那么该记录行最新的修改是可以看到的,直接返回记录行数据
6. 快照读和当前读:
① 快照读:普通的select语句(不包含select。。。lock in share mode,select … for update)
②当前读:select。。。lock in share mode,select … for update,insert,update,delete语句,这些语句获取的是数据库中最新的数据
所以只靠MVCC实现RR隔离级别,可以保证可重复度,但是并不能完全防止幻读
如:事务A执行特殊select语句,事务B,insert数据,然后事务A再查询,就会多出一条语句;因为在第一次select执行时,创建的是当前读,每次都会获取最新的数据,所以只靠MVCC是不可能完全解决幻读,所以结合了行锁和间隙锁=》Next_key lock,锁定本行,防止修改,锁定间隙,防止插入
7. RR和RC的Read View产生区别:
①在innodb中的Repeatable Read级别, 只有事务在begin之后,执行第一条select(读操作)时, 才会创建一个快照(read view),将当前系统中活跃的其他事务记录起来;并且事务之后都是使用的这个快照,不会重新创建,直到事务结束。
②在innodb中的Read Committed级别,事务在begin之后,执行每条select(读操作)语句时,快照会被重置,即会重新创建一个快照(read view)。
官方文档:consistent read,里面所说的consistent read 一致性读,我的理解就是 快照读,也就是普通select语句,它们不会对访问的数据加锁。 只有普通select语句才会创建快照,select … lock in share mode,select … for update不会,update、delete、insert语句也不会,因为它们都是 当前读,会对访问的数据加锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

booth-ZDH

爪哇一生

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值