庐山真面目"/>
MVCC的庐山真面目
概念
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
使用MVCC的好处
- 多版本并发控制(MVCC)是一种用来
解决读-写冲突的无锁并发控制
,在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。 - 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题(后面解释)。
注意
- 本文讨论的MVCC是指Mysql搜索引擎inno DB的多版本并发控制。
- MVCC只解决快照读时的读写冲突,而不解决当前读操作时的读写冲突。当前读操作通过加锁的方式解决读写冲突。并且MVCC只作用于读提交和可重复读两个事务隔离级别,因为读未提交只读最新数据,不需要版本控制。可串行化通过加锁阻塞的方式控制读写冲突。
- 快照读:简单的select操作(当然不包括 select … lock in share mode, select … for update)。
- 当前读:select … lock in share mode、select … for update、insert、update、delete。
- 数据库生成事务ID是通过当前时间戳递增方式生成的。
小结
- MVCC可以无阻塞解决读(快照读)写冲突。但是如果是当前读,读写冲突还得使用锁解决解决。
- 读读冲突不需要额外处理。
- 写写冲突也需要锁。
原则
后面说‘读’,不特别说明,都是指快照读。当前读+写操作可以暂时统一理解为‘写’。
原理
MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖undo日志 、Read View 、每行记录中的 3个隐式字段(DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID)来实现的。
undo log
undo log记录了表数据的修改记录(insert、update、delete),形成版本链。可以支持回滚操作和快照读操作
。
undo log主要分为两种
,仅update undo log支持MVCC:
- insert undo log
代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃 - update undo log
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。
三个隐式字段的概念
- DB_TRX_ID:6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID。
- DB_ROLL_PTR:7byte,回滚指针,指向这条记录的上一个版本(构成版本链)。
- DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
read view(读视图)
有了undo log提供的版本链和每行记录都存在的三个隐藏记录外,还需要生成read view作为版本链中某条记录是否可见的依据。
readview中四个比较重要的概念
- m_ids:表示在生成readview时,当前系统中活跃的读写事务id列表;
- min_trx_id:表示在生成readview时,当前系统中活跃的读写事务中最小的事务id,也就是m_ids中最小的值;
- max_trx_id:表示生成readview时,系统中应该分配给下一个事务的id值;
- creator_trx_id:表示生成该readview的事务的事务id;
有了readview,在访问某条记录时,按照以下步骤判断版本链中某个版本的记录是否可见
- 如果被访问版本的trx_id,与readview中的creator_trx_id值相同,表明当前事务在访问自己修改过的记录,该版本可以被当前事务访问;
- 如果被访问版本的trx_id,小于readview中的min_trx_id值,表明生成该版本的事务在当前事务生成readview前已经提交,该版本可以被当前事务访问;
- 如果被访问版本的trx_id,大于或等于readview中的max_trx_id值,表明生成该版本的事务在当前事务生成readview后才开启,该版本不可以被当前事务访问;
- 如果被访问版本的trx_id,值在readview的min_trx_id和max_trx_id之间,就需要判断trx_id属性值是不是在m_ids列表中?
如果在:说明创建readview时生成该版本的事务还是活跃的,该版本不可以被访问
如果不在:说明创建readview时生成该版本的事务已经被提交,该版本可以被访问;
小结
到这里我们明白了MVCC的机制,利用每行记录都有的三个隐藏字段得到当前记录的最新修改事务ID和版本链,结合生成read view时的事务ID(creator_trx_id)和read view中记录的当前活跃事务ID等信息,判断某行记录是否对当前快照读可见,达到目的。
读已提交和可重复读下的read view区别
在读已提交和可重复读中都使用MVCC控制,可为什么读已提交会造成不可重复读的问题呢?答案在生成read view的时机。
生成readview时机
- RC隔离级别:每次读取数据前,都生成一个readview。
- RR隔离级别:在第一次读取数据前,生成一个readview。
RR隔离级别表1和RC隔离级别表2中read view的生成时机对比
可以看到,表1在事务B开启的时候就自动执行了一次快照读,创建了read view,并且后续的select都使用这个read view。而表2在执行select前才创建read view,并且每次select前都创建一个新的read view。从上述的MVCC机制中可知,生成read view时机不一样,生成的当前活跃事务id列表也不一样,直接导致版本链数据可见性不一样,最终导致RR和RC两个隔离级别。
更多推荐
MVCC的庐山真面目
发布评论