一致性读的实现方式:
- 每个事务启动的瞬间 , 都会构建一个数组(m_ids) , 用来记录目前所有“活跃事务”(事务启动了 , 但是还没提交)的 ID;
- 数组中的最小事务ID为低水位;
- 数组中的最大事务ID+1为高水位;
- 数据版本可见性规则:当前数据某个版本是否可见 , 取决于当前数据的DB_TRX_ID以及这个一致性视图数组中记录的事务ID做对比来判断:低水位以前的数据版本可见 , 高水位以后的数据版本不可见 , 低水位和高水位之间得查看当前数据版本的DB_TRX_ID是否存在数组中 , 若存在意味着事务未提交 , 不可见 , 若不存在意味着事务已提交 , 可见 。
文章插图
那按照一致性读的理解 , 事务B已经创建了自己的快照数据了 , 它的输出应该是num = 2呀 , 为什么会是num=3?
可是如果不是num=3 , 那么已经提交的事务C的操作不就丢失了吗?(产生丢失更新问题)
这里又涉及到一个知识点:
更新数据都是先读后写的 , 而这个读 , 只能读当前的值 , 称为“当前读”(current read) 。
3、当前读(current reads)也叫做锁定读(locking reads)
文章插图
官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
InnoDB引擎支持两种方式的锁定读以提供额外的安全性(MySQL 5.7版本):
# 读锁(S 锁 , 共享锁)SELECT ... LOCK IN SHARE MODE;# 写锁(X 锁 , 排他锁)SELECT ... FOR UPDATE;
锁定读会在被读取的数据上加一把共享锁 , 其他事务可以读取记录 , 但是不可以修改记录 , 直到当前事务提交 。锁定读验证:
文章插图
为什么要有锁定读?
如果你在一个事务中先查询了一个数据 , 然后插入或者更新相关的数据 , 这个时候来了一个事务B同时更新或者删除你要查询的记录 , 就会出现幻读问题了 。
这也是为什么MVCC不能完全解决幻读的问题 , 而是需要MVCC+行锁+间隙锁(next-key lock)的方式 。
4、事务A、B、C的执行流程继续看开头的第一张图:
文章插图
start transaction with consistent snapshot;
这条SQL语句可以立即启动事务 , 创建当前事务的一致性读快照 。效果等同于start transaction然后马上执行select语句 。我们接下来看看文章开头的三个事务对数据行的修改流程 , 按照步骤1~6的操作如下:
文章插图
如果大家细致的查看上图的三个事务的穿插执行流程 , 可以发现 , A、B、C三个事务无论是commit还是rollback , 都是可以最终得到正确的数据 。
这就是InnoDB引擎下的多版本并发控制(MVCC)的实现原理 。
总结以下几个关键点:
- 每一个事务都会创建一个数据快照 , 快照创建的时机根据隔离级别的不同有所区别;
- 每一个事务都会生成一个全局唯一的DB_TRX_ID , 用于标记当前版本;
- DB_ROLL_PTR是回滚指针的意思 , 结合DB_TRX_ID来最终确定我要拿到的数据;
- DB_TRX_ID、DB_ROLL_PTR、undo log这三个值来控制数据的版本;
- update、delete操作都是先读后写 , 这个读属于锁定读(当前读) 。
- 《MySQL实战45讲》
- 《高性能MySQL 第二版》
- MySQL官网:https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html
- 淘宝数据库内核月报 - 2017 / 12:http://mysql.taobao.org/monthly/2017/12/01/
推荐阅读
- 丝绸之路是在哪个时期开通的 丝绸之路最早是由谁开通的
- 黄山毛峰是绿茶么,绿茶之黄山毛峰
- 数据库:innodb数据组织形式
- 方腊之战中梁山死了几名好汉 梁山好汉有哪些被方腊杀死
- 嵇康是建安七子之一吗? 王粲是什么七子之一
- 清平乐村居表达了诗人什么之情 清平乐村居词人在词中勾勒了一幅怎样的画面
- 蓝田之战楚国如果赢了 秦楚蓝田之战真相
- |钓鱼高手轻易不告诉别人的5个钓鱼秘诀,掌握之后才能成为高手
- 后汉书王梁传 东汉梁太后之兄
- 赤壁之战之后形成了什么局面 赤壁之战形成了三国鼎立的局面吗