MVCC机制| 青训营笔记

106 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天

使用场景:

并发事务大致可以分为4类,读读,写写,读写,写读。读读不会影响数据。

  • 读读场景:不会引起数据的变化,不用做任何处理。
  • 写写场景:会有更新覆盖的问题,需要进行处理,需要进行上锁处理。
  • 读写写读场景:两种类似,写读,会有脏读,不可重复读,幻读出现,需要处理,使用的是mvcc机制处理。

MVCC机制:

多版本并发控制技术,主要用来提升数据库性能设计的,处理读写并发冲突。主要是在InnoDB中使用。

MySQL中仅在RC读已提交级别、RR可重复读级别才会使用MVCC机制。

实现原理-三种支持技术:

通过MVCC机制的隐藏字段,Undo-log日志和ReadView三个东西实现。

隐藏字段:

主要有DB_ROW_ID、DB_Deleted_Bit、DB_TRX_ID、DB_ROLL_PTR这四个隐藏字段。

隐藏主键-RowID:聚簇索引没有指定主键会选取一个合适的作为主键或者生成,也就是这个主键

没有主键选取规则:1自增键位,2非空字段,3创建一个递增的Row_ID作为聚簇索引列

删除标识-Deleted_Bit:先设置为1,用于标识有事务将该条数据标识为delete了,其他sql不会将这条数据作为结果。如果事务成功则等待purger来进行删除,如果事务失败回滚,设置为0就好。用这个机制的优点在于,不用真正再索引中删除,使得树结构改变。

purger删除机制,在设置了删除标识为1后,会自动删除缓存树,防止占用过多的磁盘,purger自身维护了一个ReadView。

最近更新的事务ID-TRX_ID(6Byte) :对于每一个创建的事务都会有一个事务ID,事务ID递增,除了查询的事务ID=0,用于记录事务的ID

回滚指针-ROLL_PTR(7Bytes): 每个事务都会有一个回滚指针,老版本的数据在Undo-log中,rollback_pointer就是地址指针,指向undo-log中的旧版本的数据。需要回滚事务就需要用到这个隐藏列。

undo-log日志:

undo-log记录了旧版本的数据,这个是一个版本链,不止一个版本的数据,只要有修改操作就会被记录,串起来的版本链,最新的旧数据在链表头。版本链就是多版本。

对update语句举例:

先在数据上加排他锁,然后数据拷贝到undo-log,然后数据修改,设置trx_id为当前事务ID,隐藏字段的roll_ptr,然后提交事务释放锁。

ReadView:

就是一个事务读取一条数据时,MVCC对Mysql的运行状态生成一个快照,就是ReadView,一个select事务开始后,就会生成一个ReadView,包含四个部分:

  • create_trx_id:ReadView的事务ID
  • trx_ids:生成当前Read_View时,系统活跃的事务的ID列表。
  • up_limit_id:活跃事务列表中最小的ID
  • low_limit_id:生成当前Read_View时,系统分配的下一个事务ID。

实现原理-运行原理:

  • 事务进行修改操作,将旧数据放入Undo-log日志中。
  • 事务进行查询操作,MVCC会生成一个ReadView快照。

undo-log主要存储数据的历史版本,ReadView主要进行多版本并发控制。

就是首先会去获取表中行数据的隐藏列,然后经过上述一系列判断后,可以得知:目前查询数据的事务到底能不能访问最新版的数据。如果能,就直接拿到表中的数据并返回,反之,不能则去Undo-log日志中获取旧版本的数据返回。如果Undo-log日志中的旧数据存在一个版本链时,此时会首先根据隐藏列roll_ptr找到链表头,然后依次遍历整个列表,从而检索到最合适的一条数据并返回。

  • ReadView.trx_ids中的数据时旧数据不能读取。因为还没有提交。

select查询到新添加的数据,这时候roll_ptr=null,不会读取到这条数据。查询数据的事务不能读取最新版数据,同时又无法从版本链中找到旧数据,那就意味着这条数据对T1事务完全不可见。

RC,RR不同级别下的MVCC机制:

MySQL的事务隔离机制处于RC读已提交级别。

总结:

MVCC多版本并发控制,就是通过undo-log记录历史版本数据,然后通过隐藏字段+ReadView快照进行回滚和并发处理逻辑。