MVCC -- 小结
MVCC -- 多版本并发控制

快照读 和 当前读
-
快照读 -- 可能是历史数据;当前读 -- 读取的是最新的数据
-
快照读
当执行
select ....时为快照读,数据是从ReadView中读取的 -
当前读
当执行的是
select .... lock in share modeselect .... for updateupdate,delete,insert等这些操作时,会对表数据进行加锁操作,每次读取到的数据都是最新的数据
事务ACID的实现

InnoDB对MVCC的实现方式
-
隐藏字段
在内部,
InnoDB存储引擎为每行数据添加了三个 隐藏字段 :DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete操作在内部被视为更新,只不过会在记录头Record header中的deleted_flag字段将其标记为已删除DB_ROLL_PTR(7字节)回滚指针,指向该行的undo log。如果该行未被更新,则为空DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB会使用该 id 来生成聚簇索引

-
ReadView
事务在进行快照读时产生的视图表,主要是用来做可见性判断,里面保存了 "当前对本事务不可见的其他活跃事务"
主要有以下字段:
m_ids:Read View创建时其他未提交的活跃事务 ID 列表。创建Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。m_ids不包括当前事务自己和已提交的事务(正在内存中)m_up_limit_id:活跃事务列表m_ids中最小的事务 ID,如果m_ids为空,则m_up_limit_id为m_low_limit_id。小于这个 ID 的数据版本均可见m_low_limit_id:目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于这个 ID 的数据版本均不可见m_creator_trx_id:创建该Read View的事务 ID,在不同隔离级别下不同

-
undo-log
undo log主要有两个作用:- 当事务回滚时用于将数据恢复到修改前的样子
- 另一个作用是
MVCC,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过undo log读取之前的版本数据,以此实现非锁定读 
-
可见性算法
在
InnoDB存储引擎中,创建一个新事务后,执行每个select语句前,都会创建一个快照(Read View),快照中保存了当前数据库系统中正处于活跃(没有 commit)的事务的 ID 号。其实简单的说保存的是系统中当前不应该被本事务看到的其他事务 ID 列表(即 m_ids)。当用户在这个事务中要读取某个记录行的时候,InnoDB会将该记录行的DB_TRX_ID与Read View中的一些变量及当前事务 ID 进行比较,判断是否满足可见性条件- 如果记录
DB_TRX_ID<m_up_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之前就提交了,所以该记录行的值对当前事务是可见的 - 如果
DB_TRX_ID>=m_low_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之后才修改该行,所以该记录行的值对当前事务不可见。跳到步骤 5 m_ids为空,则表明在当前事务创建快照之前,修改该行的事务就已经提交了,所以该记录行的值对当前事务是可见的- 如果
m_up_limit_id<=DB_TRX_ID<m_up_limit_id,表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照的时候可能处于“活动状态”或者“已提交状态”;所以就要对活跃事务列表 m_ids 进行查找(源码中是用的二分查找,因为是有序的)- 如果在活跃事务列表 m_ids 中能找到
DB_TRX_ID,表明:① 在当前事务创建快照前,该记录行的值被事务 ID 为DB_TRX_ID的事务修改了,但没有提交;或者 ② 在当前事务创建快照后,该记录行的值被事务 ID 为DB_TRX_ID的事务修改了。这些情况下,这个记录行的值对当前事务都是不可见的。跳到步骤 5 - 在活跃事务列表中找不到,则表明“id 为
trx_id的事务”在修改“该记录行的值”后,在“当前事务”创建快照前就已经提交了,所以记录行对当前事务可见
- 如果在活跃事务列表 m_ids 中能找到
- 在该记录行的
DB_ROLL_PTR指针所指向的undo log取出快照记录,用快照记录的DB_TRX_ID跳到步骤 1 重新开始判断,直到找到满足的快照版本或返回空
- 如果记录
ReadView 和 可见性算法 得出:事务的开启时机 和 事务的提交 是影响事务至关重要的因素
不同隔离级别下MVCC的区别
在事务隔离级别 RC 和 RR (InnoDB 存储引擎的默认事务隔离级别)下,InnoDB 存储引擎使用 MVCC(非锁定一致性读),但它们生成 Read View 的时机却不同
- 在 RC 隔离级别下的
每次select查询前都生成一个Read View(m_ids 列表) - 在 RR 隔离级别下只在事务开始后
第一次select数据前生成一个Read View(m_ids 列表) 
二阶段提交
- 为什么需要二阶段提交


MVCC如何在RR隔离级别下解决幻读
InnoDB存储引擎在 RR 级别下通过 MVCC和 Next-key Lock 来解决幻读问题:
-
为什么会产生幻读:
当有事务进行了更新操作并提交时,会触发当前读,此时下一个事务进行select时,会重新生成ReadView,此时就可能发生幻读,以为前后读出的数据行数不一样(注意事务的开启时机)
-
执行普通
select,此时会以MVCC快照读的方式读取数据在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成
Read View,并使用至事务提交。所以在生成Read View之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读” -
执行 select...for update/lock in share mode、insert、update、delete 等当前读
在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!
InnoDB使用 Next-key Lock 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读 - - 间隙锁

浙公网安备 33010602011771号