innoDB MVCC 实现原理
innoDB通过mvcc(多版本并发控制)来提高并发能力,通过版本快照,保证大部分读操作都不用加锁,性能很好。缺点是缺点是没行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作。
MVCC只在read commited和repeatable read隔离级别下生效,其他两个隔离级别与MVCC不兼容,因为read uncommited总是读最新的数据行,而串行化会对所有读取的行加锁。
innoDB的行数据有多个版本,每个版本有自己的row trx_id,每个事务或者语句有自己的一致性视图。普通查询是一致性读,一致性读会根据row trx_id和一致性视图确定数据版本的可见性。
- 对于rr级别,查询只承认在事务启动前就已经提交完成的数据。
- 对于rc级别,查询只承认在语句启动前就已经提交完成的数据。
实现上的区别:
- rr级别,只需要在事务启动时创建一致性视图,事务的语句共用这一个一致性视图。
- rc级别,每个语句执行前都会算出一个新的视图。
MVCC实现原理
innoDB 通过通过在每行记录后面保存两个隐藏列、read-view 和 undolog 版本链 实现MVCC。
这两个列,一个保存创建行时的事务版本号,一个保存删除行时的事务版本号。
innoDB每个事务都有一个唯一的事务ID,叫transaction ID。事务开始时向innoDB申请的,按申请顺序严格递增。
当创建一行记录时,会将事务ID赋值给这个行的创建事务版本号,记为row trx_id。
当更新时,同时更新两个事务版本号。
也就是说,表中每一行都有多个版本(row),每个版本都有自己的row trx_id,都对应一个事务ID。
在实现上,innoDB为每个事务生成一个数组,用来保存这个事务启动瞬间,当前正在”活跃“的所有事务ID。活跃是指启动了还没提交。
数组里最小值记为低水位,当前事务最大值+1记为高水位。
这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。
数据版本的可见性规则,就是基于数据的row trx_id 和一致性视图的对比结果得到的。
对于当前事务的启动瞬间来说,一个数据版本的row trx_id,有以下几种可能:
- 落在绿色部分,表示这个版本是已经提交的事务或自己生产的,可见。
- 落在红色部分,表示这个版本是由将来启动的事务生成的,不可见。
落在黄色部分,包括两种情况:
- 若row trx_id不在数组内,表示这个版本是由未提交事务生成的,不可见;
- 若row trx_id在数组内,表示这个版本是已经提交事务生成的,可见。
在不同隔离级别下的区别:
read commited 隔离级别下。
- SELECT
读取最新提交的版本数据。
- SELECT
repetable read 隔离级别下。
- SELECT
根据两个条件检查每条记录:
- 只查早于当前事务版本的数据行(也就是行的版本号小于或等于事务版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或修改过的。
- 行的删除版本要么未定义,要么大于当前事务版本号,这样可以确保事务读取的行,在事务开始前未被删除。
- INSERT
为新插入的每一行保存当前系统版本号作为行版本号。 - DELETE
为删除的每一行保存当前系统版本号作为行的删除版本号。 - UPDATE
innoDB为新插入的行,保存当前系统版本号作为行版本号,同时,保存当前系统版本号到原来行的删除版本号。
- SELECT