MySql-MVCC 详细介绍

752 阅读2分钟

先来解释下MVCC是什么:
多版本并发控制,基本思想是每次事务都生成一个新版本的数据,在读数据时选择不同的版本读取。既可以实现对事务结果完整性的读取,也能提升并发的读写性能;

先回顾下MySql事务中的隔离级别;
读未提交、读已提交、可重复读、串行化
MVCC主要针对的是 读已提交 可重复读这两种隔离级别;

MySql每行数据默认都有两个隐藏列:事务ID、回滚指针(指向前一个版本)

查询数据的时候,有一个 read-view ,用来确定读取 undo.log 中哪个版本的数据;
下面直接举例,开启四个事务:(以下操作均基于此图)

事务1,把 name 值改为了 'A',此时这行数据为:

事务2,把 name 值改为了 'B',此时这行数据为:

此时,回滚指针指向了前一条记录;
事务3,把 name 值改为了 'C',此时这行数据为:

此时,回滚指针指向了前一条记录;
事务1,把 name 值改为了 'D',此时这行数据为:

此时,回滚指针指向了前一条记录;
由图可知,已经形成了一个版本链

先说 读已提交:
执行事务4,查询操作,会生成一个read-view,里面包含所有未提交事务的数组,和当前最大的事务。select 时,会根据read-view从版本链最新数据依次往下找,拿到 trx_id 判断是否在 read-view 范围内:

此时,如果 trx_id 比 1 小,可读,如果 trx_id 比 3 大,说明是新生成的,不可读,如果 trx_id 在 [1,3] 范围内,说明是未提交事务,不可读;所以,事务4中第一条select 查询到的结果,就是 name = B;第二条select 查询到的结果,就是 name = C

可重复读 和 读已提交 区别:
读已提交:每次select时,都会生成新的 read-view;
可重复读:每次select时,复用第一次的 read-view;

总结一下:
如果当前记录:事务id < 未提交事务的最小id,则可读;
如果当前记录:最小id <= 事务id <= 最大id,判断事务id是否在未提交事务id数组中,若不在则可读;
如果当前记录:事务id > 最大id,则不可读;