MVCC之InnoDB实现

  • 先给出几个概念
  1. mysql中的RR隔离级别不会出现幻读。
  2. MVCC控制过程当中,mysql会往每条数据附加三个列,分别是DB_TRX_ID(最后更新(insert/update/delete)数据的事务id与一个标志位标识该记录是否为删除记录)、DB_ROLL_PTR(回滚指针)与DB_ROW_ID(自增,用于标识数据的更新时间).
  3. 每一个事务开始时系统会分配一个事务号,该事务号递增。
  4. MVCC(RR级别)中的读操做分为快照读(极可能是历史数据)以及当前读,其中当前读包括select * ... for update;insert;delete等,它读的是最新数据(由锁来实现),RR隔离级别实现的效果是对两种读都成立,即快照读中不会有幻读以及不可重复读,在当前读也不会出现幻读或不可重复读。
  • 快照读实现RR效果
    快照读便是select * from table where condition;没有for update等语句。
    在InnoDB中经过一个ReadView的数据结构来实现快照。该数据结构中有如下数据,max_tx_id、min_tx_id、exclude_tx_ids.该数据结构在一个事务中的第一次SnapShot调用中建立,并在该事务中再也不改变。其中max_tx_id通常取快照读时,系统的事务号,全部大于该事务号的事务数据,都是在当前快照后建立的,对于当前快照应该是不可见的。min_tx_id,指示了全部的可用数据的事务id的最大值,其通常取活动事务(未提交)的最小值,exclude_tx_ids指向全部的不可见事务id,通常是建立SnapShot时的全部活动事务。如下对update/delete/insert分别介绍其执行规则,说明为何ReadView能知足RR效果。
//如下操做都会在得到锁后当即执行,没必要等到事务提交。
//insert:插入一条数据,将DB_TRX_ID设置为当前事务id,DB_ROLL_PTR指向上一条数据。
//delete:将DB_TRX_ID设置为当前事务id并标记为删除(新增一条),并设置DB_ROLL_PTR。
//update:先把本来数据的DB_TRX_ID设置为当前事务id并标记为删除(新增一条),而后执行一个insert操做。
//select,经过ReadView能够很快判断出一条数据的DB_TRX_ID所对应的事务对于当前快照是否可见,而后利用DB_ROW_ID,往回查找全部可见数据中的最新数据。
eg:假设事务t2在事务t1快照以后启动,因此
|val1|val2|DB_TRX_ID|DB_ROW_ID|
初始数据:|-|-|1|1|
t1执行SnapShot,标志t2_id不可见。
t2更新数据,数据记录变动为如下
|-|-|10|3|
|-|-|10d|2|
|-|-|1|1|
t1再次读取时,因为知道DB_TRX_ID为10的事务数据是不可见的,因此直接略过两条,读取了|-|-|1|1|即此时只有该数据是有效数据。固然若是读到的最新有效数据是一条删除数据(d),则这次读取无结果。因为insert使用了不可见事务的id,因此会被忽略,因此无幻读。
  • 当前读的RR效果实现则由锁进行保证,详细可见该博客
  • InnoDB的RR以及RC级别,差异就在于RR中存在着GAP锁,这在阻止某些insert语句的同时,也阻止了update与delete语句(update本质上也是insert).
  • InnoDB的锁操做决定了,其在RC/RR隔离等级下都不可能出现第一类更新丢失。可是都存在第二类更新丢失(除非使用当前读(for update)读取数据),但更好的方法应该是从程序中使用乐观版本控制(添加version列)。
相关文章
相关标签/搜索