值得收藏,揭秘 MySQL 多版本并发控制实现原理
▲ 点击上方“架构精进之路”关注公众号
回复“01”领取「程序员进阶大礼包」

架构精进之路
十年研发风雨路,大厂架构师,CSDN博客专家。专注软件架构研究,技术学习与职业成长,坚持分享接地气儿的架构技术干货文章!
公众号
这是「架构精进之路」公众号的第73篇原创文章
一、MVCC出现背景是什么?

- 脏读:一个事务读取到了另外一个事务没有提交的数据;
- 不可重复读:在同一事务中,两次读取同一数据,得到内容不同;
- 幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同。
- 乐观锁:
其实现如同它的名字一样,是假设比较好的情况。每次取数据的时候都认为他人不会对其修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。 -
悲观锁:
悲观锁也如同它的名字一样,总是假设比较坏的情况,每次取数据的时候都认为他人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。
二、什么是MVCC,它解决了什么问题?
-
快照读:
读取的是快照数据,不加锁的简单的SELECT都属于快照读(只是普通的读操作)。 - 当前读:
当前读就是读取最新数据,而不是历史版本的数据。加锁的SELECT,或者对数据进行增删改都会进行当前读(包括加锁的读取和DML操作)。
三、应用举例分析


Case2:当我们读取的时候用了加行锁,可能会出现死锁的情况,如下图所示。

比如当我们读到 A 有 1000 元的时候,此时 B 开始执行给 A 转账。
四、InnoDB如何实现MVCC?
- 首先获取事务自己的版本号,也就是事务 ID;
- 获取 Read View;
- 查询得到的数据,然后与 Read View 中的事务版本号进行比较;
- 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;
- 最后返回符合规则的数据。

- up_limit_id,活跃的事务中最小的事务 ID;
- trx_ids,系统当前正在活跃的事务 ID 集合;
- low_limit_id,活跃的事务中最大的事务 ID;
- creator_trx_id,创建这个 Read View 的事务 ID。
- DB_ROW_ID :
6-byte,记录操作该数据事务的事务ID;
- DB_TRX_ID :
6-byte,当创建表没有合适的索引作为聚集索引时,会用该隐藏ID创建聚集索引;
- DB_ROLL_PTR :
7-byte,回滚指针,指向上一个版本数据在undo log 里的位置指针;


- 记录锁:
针对单个行记录添加锁。
- 间隙锁(Gap Locking):
可以锁住一个范围(索引之间的空隙),但不包括记录本身。
采用间隙锁的方式可以防止幻读情况的产生。
- Next-Key 锁:
锁住一个范围,同时锁定记录本身,相当于间隙锁 + 记录锁,可以解决幻读的问题。

六、总结
MVCC 的核心就是 Undo Log+ Read View。
-
“MV”就是通过 Undo Log 来保存数据的历史版本,实现多版本的管理;
-
“CC”是通过 Read View 来实现管理,通过 Read View 原则来决定数据是否显示。
同时针对不同的隔离级别,Read View 的生成策略不同,也就实现了不同的隔离级别。
🎉 福利:关注公众号回复关键字:MySQL,即可免费获取《高性能MySQL 第3版》一套
·················· END ··················
关注公众号,免费领学习资料

架构精进之路
十年研发风雨路,大厂架构师,CSDN博客专家。专注软件架构研究,技术学习与职业成长,坚持分享接地气儿的架构技术干货文章!
公众号
十年研发路,大厂架构师,CSDN博客专家
专注架构技术学习及分享,职业与认知升级
坚持分享接地气儿的干货,期待与你一起成长
「架构精进之路」专注架构研究,技术分享