MVCC(Multi-VersionConcurrency Control 多版本并发控制)
概念
MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。
MVCC的实现,是通过保存数据在某个时间点的快照来实现的。即为:不管需要执行多长时间,每个事务看到的数据都是一致的。
在Mysql的InnoDB引擎中就是指在已提交读(READ COMMITTD)和可重复读(REPEATABLE READ)这两种隔离级别下的事务对于SELECT操作会访问版本链中的记录的过程。
这就使得别的事务可以修改这条记录,反正每次修改都会在版本链中记录。SELECT可以去版本链中拿记录,这就实现了读-写,写-读的并发执行,提升了系统的性能。
具体实现
innodb的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个是行的创建时间,一个保存行的过期时间。存储的是系统版本号,不是真实的时间。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
trx_id(事务id)
这个id用来存储的每次对某条聚簇索引记录进行修改的时候的事务id。
roll_pointer(回滚id)
每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)
执行update修改数据,相当于在数据库里新增了一条数据1,将原来的旧数据2存储到undo日志中,在roll pointer 也就是事务id处记录下uodo日志的指针,通过这个指针找到原来的记录。
Mysql在第一次查询的时候会生成一个一次性视图,叫做read-view.
ReadView
已提交读和可重复读的区别就在于它们生成ReadView的策略不同。
当执行查询sql会生成一致性视图read-view,它由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已经创建的最大事务的id组成,查询的数据结果需要跟read-view做对比从而得到快照结果。
ReadView中主要就是有个列表来存储我们系统中当前活跃着的读写事务,也就是begin了还未提交的事务。通过这个列表来判断记录的某个版本是否对当前事务可见。
列如
Readview[100,300]
1、 如果trx_id<min_id.表示这个版本是已提交事务生成的,所以对当前活动的事务来说是可访问的。
2、 如果trx_id>max_id,表示这个版本是ReadView生成之后才发生的,不能被访问。
3、 如果min_id<trx_id<max_id,
a. 若trx_id在数组中,表示这个版本是还未提交的事务生成的,所以版本不能被访问。
b. 若trx_id不在数组中,则事务已提交,可以被访问。
已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,
可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别。
MVCC只在repeatable read和read committed两个隔离级别下工作。其他两个隔离级别和MVCC不兼容。因为READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁。