mysql的MVCC机制

50 阅读4分钟

一、什么是MVCC机制

所谓的MVCC即Multi-Version Concurrency Control,即多版本并发控制技术,主要为了提升数据库并发性能而设计,用于处理读-写并发冲突问题,做到对于读写冲突无需使用加锁也能解决问题,从而达到在任何时刻读操作都是非阻塞的
MySQL的MVCC机制实际上只有InnoDB实现了,其只在数据库隔离级别为RR、RC的情况下才会使用,原因在于对于RU允许脏读问题的存在,那就自然无需进行版本控制,也就无需MVCC的介入,而对于Serializable串行化隔离级别,因为将所有的并发事务串行化处理,也就意味着所有的事务执行都是按照顺序一个个执行,不存在多线程并发问题,也就无需MVCC的介入

二、MVCC实现原理

2.1 InnoDB表行数据隐藏字段

2.1.1 隐藏主键-ROW_ID

InnoDB引擎表数据按照聚簇索引格式存储,因此都会选择主键作为聚簇索引列,然后基于主键字段构建索引树,但如果表未设置主键,默认会选择具备唯一非空属性的字段,作为构建树的聚簇索引字段

2.1.2 删除标识-Deleted_Bit

对于delete语句,执行完成之后不会立刻删除数据,而是将这条数据的Deleted_Bit标识修改为1/true,后续SQL检索,会过滤带有该标识的数据行,这样设计有两个好处

  • ①减少因数据删除而导致树结构破坏,导致出现叶子节点合并的情况
  • ②避免事务回滚时,重新插入数据,叶子节点分裂

2.1.3 最近更新事务ID-TRX_ID

Mysql对于每一个创建的事务都会分配一个事务ID,ID分配遵循递增原则,写事务的ID分配为递增,而对于select语句,其分配的事务ID为0,但对于手动事务分配ID仍为自增,即使该事务只有写入操作,该字段是实现MVCC的关键

2.1.4 回滚指针-ROLL_PTR

全称rollback_pointer,表中每条数据都会存在的隐藏字段,当一个事务修改某一行数据之后,旧版本数据会存储在undo-log中,新版本数据的回滚指针会映射到undo-log中旧版数据上,依次形成一种链式结构,需要回滚数据时,依据这个隐藏列就可以找到改动前的数据进行回滚

2.2 InnoDB引擎的undo-log日志

undo-log内部依据roll_ptr构建了一系列数据的版本链,最新的旧版本的数据,会插入到链表头部中,其更新数据流程如下
①对ID=1这条要更新的数据上排它锁
②将原本的旧数据拷贝到undo-log的rollback segement区域
③对表数据进行修改,修改完成之后更新隐藏字段trx_id改为当前事务ID
④将隐藏字段roll_ptr指向undo-log中对应的旧数据,提交事务释放锁

2.3 ReadView

当一个事务尝试读取一条数据时,MVCC基于当前MySQL的运行状态生成快照,也被称为读视图,在这个快照中记录着当前活跃的事务id,其包含四个核心内容

  • creator_trx_id:代表当前这个ReadView的事务ID
  • trx_ids:生成ReadView时,系统内活跃的事务id
  • up_limit_id:活跃的事务里列表中,最小的事务id
  • low_limit_id:生成当前ReadView时,系统要给下一个事务分配的id值

2.4 MVCC机制实现原理

  • ①当事务中出现select语句时,生成ReadView
  • ②判断trx_id与ReadView.creator_trx_id是否相同:
    • 相同:代表创建readView和修改行数据事务相同,可以读取最新数据
    • 不相同:代表要查询的数据,被其他事务修改过,继续判断
  • ③判断trx_id是否小于ReadView.up_limit_id最小活跃id
    • 小于:代表改动行数据的事务在创建快照前就已经结束,可以读取最新版本的数据
    • 不小于:改动事务还在执行,继续判断
  • ④trx_id是否小于ReadView.low_limit_id
    • 小于:改动行数据在low_limit_id和up_limit_id之间,进一步判断
    • 不小于:改动数据的事务在快照后开启,无法访问最新数据
  • ⑤trx_id是否在trx_ids中
    • 在:改动行数据的事务还在执行,无法访问最新数据
    • 不在:已经结束,可以访问最新数据 MVCC.png

2.5 undo log旧版数据如何获取

判断标准:旧版本的数据,其隐藏列trx_id不能在ReadView.trx_ids活跃列表中,依照此标准在链表中找到满足条件的数据

三、 RC、RR不同级别的MVCC

RC级别下,MVCC在每次select语句执行之前都会生成一个ReadView,而在RR级别下,只会在首次执行select语句时生成快照,后续select基于这个快照判断