参考:
前言
Multiversion concurrency control 多版本并发控制当部分数据在被某个事务处理的同时,有其他事务对这些数据进行访问,则会使用多版本并发控制来避免由于写操作的堵塞,引发的读操作失败的并发问题。
mysql 默认的隔离级别为RR(Repeatable Read),理论上(具体可以查看我的前一篇文章ACID),在此隔离级别下,不可重复读是不会发生的。
当我们分别启动两个事务
事务一执行以下命令:
UPDATE test_zq SET test_id = 20 WHERE id = 1;
事务二执行以下命令:
SELECT * FROM test_zq WHERE id = 1;

(图片出自【MySQL(5)| 五分钟搞清楚 MVCC 机制】)
根据事务隔离级别来看,事务二理论上获得 X 锁,此时事务一是无法再获取到 S 锁去读取 id=1 的数据的,但它却读到了。
通过结果说明:我们可以在一个事务未进行 commit/rollback操作之前,另一个事务仍然可以读取到数据库中的数据,只不过是读取到的是其他事务未改变之前的数据。此处是利用了MVCC多数据做了多版本处理,读取的数据来源于快照。
什么是MVCC
MVCC,Multi-Version Concurrency Control,多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。
如果有人在读数据的同事,另外一个人同时在修改相同的数据,那么此时前者可能会读到脏数据,或者「半写」的数据。解决这种冲突最简单的办法就是加 X 锁,但如果加排他锁会造成较为严重的性能问题,会阻塞其他读事务的进行。而MVCC使用了多版本快照的机制解决了这一问题。写操作在完成之前,读操作读取到的都是之前的版本,写操作在提交之前,对于其他的读者来说是不可见的。
那么此处需要注意的点就是一下两个:
- 在读写并发的过程中如何实现多版本
- 如何清除旧版本数据
MVCC数据的逻辑插入
在Innodb中建表时,每个表都会有三个隐藏列,其中和MVCC有关的是
- 数据行版本号(DB_TRX_ID)
- 删除版本号(DB_ROLL_PT)(原博主没说,但从名字来看,删除版本号应该是个指针)
我们先往表中插入两条数据:
begin;-- 获取到全局事务IDinsert into `test_zq` (`id`, `test_id`) values('5','68');insert into `test_zq` (`id`, `test_id`) values('6','78');commit;-- 提交事务

可以看到,插入的过程中,会把全局的事务ID插入到数据行版本号中。
MVCC数据的逻辑删除
对上述数据做删除逻辑,执行以下SQL语句(假设此时事务ID为3)
begin;--获得全局事务ID = 3delete test_zq where id = 6;commit;
执行完之后的数据如下:
可以看到,并没有直接将数据删除,而是将 DB_ROLL_PT 字段设置为3
MVCC数据的逻辑修改
执行以下SQL语句:
begin;-- 获取全局系统事务ID 假设为 10update test_zq set test_id = 22 where id = 5;commit;
原数据ROLL_PT值被修改为10,并且重新插入一条TRX值为10的新数据
MVCC数据的逻辑查询
查询规则如下:
- 查询
DB_TRX_ID<=current_transaction_NO,查询当前事务开始前已经存在的数据,或者被当前事务修改过的数据 - 查询
DB_ROLL_PT==NULL||DB_ROLL_PT>current_transaction_NO,保证查询到的数据是在当前事务开始前没有被删除的
