传统mvcc和innodbmvcc实现
根据《高性能mysql》一书的描述,mvcc实现应该大致如下:
1、每行数据后面有两个隐藏的字段,一个字段为更新标识,一个字段为删除标识,记录内容为更新/删除时候的系统版本号。
2、select过程中,会根据当前事务版本号去过滤小于等于的数据。
3、update操作实际会新增一行记录,而并非在原数据上修改。
4、delete也并非物理删除,而是在删除标识字段上加上版本号。
听起来蛮不错的,然而innodb的mvcc实现并非如此!!
在innodb中,两个隐藏字段分别为DATA_TRX_ID
、DATA_ROLL_PTR
(如果没有主键,则还会多一个隐藏的主键列)。
DATA_TRX_ID
记录最近更新这条行记录的事务 ID
,大小为 6
个字节
DATA_ROLL_PTR
表示指向该行回滚段(rollback segment)
的指针,大小为 7
个字节,InnoDB
便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在 undo
中都通过链表的形式组织,在网上搜了一下,大概格式如下,它记录的是每个版本被修改的数据,并非是一条操作的反操作。
所以,实际上每次进行update操作时,并非是会创建一个新的副本。而是会先将就的数据行copy进undo log中去,随后修改该数据行,并且修改其DATA_TRX_ID 和 DATA_ROLL_ID。
上面说了支撑mvcc功能的列结构,那么下来看一下mysql的四种事务隔离级别和mvcc的关系。
1.脏读
和mvcc没有关系,每次select最新的数据,别的事务的数据只要已经物理写入或者修改,那么它便会去读,直接忽略掉后面的DATA_TRX_ID。
2.提交读
在mvcc的使用上,innodb引入了ReadView 这一方式。在生成一个ReadView的时候,会收集所有目前所有活跃事务(既还未提交的事务)的版本号到一个队列中m_ids,并且把当前系统版本号+1,也加入队列中。那么就可以找到版本号的最小值up_limit_id和最大值low_limit_id。在select的过程中,对于一条数据,与m_ids对比,会有如下结果。
a.小于up_limit_id,说明该事务在ReadView开启之前已经提交,所以为有效数据。
b.大于low_limit_id,说明该事物是在ReadView开启之后才提交的,为无效数据,这时候就要顺着undo log链表向上查找了,直到找到有效的
c.如果 up_limit_id<id<=low_limit_id,这个时候就要拿到id在m_ids中做比较,如果找到了相同的,则说明ReadView建立时,该事务还未提交,所以为无效数据,如果找不到,则为有效数据。数据无效处理方法同b。
ps.在undo log上查找时,并不是只有data_trx_id小于up_limit_id才为有效,满足条件c也是可以的。
以上是 传统mvcc和innodbmvcc实现 的全部内容, 来源链接: utcz.com/z/510444.html