MySql MVCC 多版本并发控制

背景

  • MySql 的大多数事务型存储引擎实现的其实都不是简单的行级锁。基于提升并发性能的考虑,它们一般都实现了多版本并发控制(MVCC)。
  • 可以认为 MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只是锁定必要的行。
  • MVCC 的实现方式有很多中,典型的有乐观并发控制和悲观并发控制。
  • MVCC 只在 READ COMMITTED 读已提交 和 REPEATABLE READ 可重复读两个隔离级别下工作。其他两个隔离级别和 MVCC 不兼容,因为 READ UNCOMMITED 读未提交总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE 串行化会对所有读取的行都加锁。

总结

  1. MVCC 是被 MySql 中 事务型存储引擎 InnoDB所支持的。
  2. 应对高并发事务,MVCC 比单纯的加行锁更有效,开销更小。
  3. MVCC 只在 READ COMMITREPEATABLE READ两个隔离级别下工作。
  4. MVCC 可以使用乐观和悲观来实现。

分析

  1. InnoDB 存储引擎在数据库没行数据后面添加了三个字段。

    • 6个字节的事务 ID(DB_TRX_ID)字段:标记了最新更新这条行记录的 transaction id,每处理一个事务,其值自动 +1。
    • 7个字节的回滚指针(DB_ROLL_PTR)字段:指向当前记录的 rollback segment 的 undo log(撤销日志记录),找之前版本的数据就是通过这个指针。
    • 6个字节的 DB_ROW_ID 字段:当由 innodb 自动产生聚集索引时,聚集索引包括这个 DB_ROW_ID 的值,否则聚集索引中不包括这个值,这个用于索引当中。
  2. 下面来演示一下事务对行记录的更新过程。

    !

  3. read view

    在 innodb 中,每创建一个新事物,存储引擎都会将当前系统中的活跃事务列表创建一个副本(read view),副本中保存的是系统中当前不应该被本事务看到的其他事务 id 列表。

    当用户在事务中要读取某行记录的时候,innodb 会将该行的当前版本好与 read view 进行比较,比较时通过比较算法。

比较算法

假设该行当前的事务 id 为 trx_id_current,read_view 中该行最早的事务 id 为 trx_id_first,最迟的事务 id 为 tax_id_last。

  1. 如果 trx_id_current < trx_id_first,那就表示

    当前事务在读取该行记录时,事务 ID 比其他的都要小。那就意味着,当前所有和该行记录有关的事务中,当前事务是第一个读取到该行记录的,没有任何在当前事务前面对该行数据做过更改但还没有提交的事务,所以当前事务可以直接拿到表中的稳定的数据!

  2. 如果 trx_id_current > tax_id_last,那就表示

    当前事务在读取该行记录时,事务 ID 比其他的 read_view 的事务 ID 都要大。那就意味着,当前所有和该行记录有关的事务中,当前事务是最后一个读取到该行记录的。需要根据该行记录的 DB_ROLL_PTR 指针所指向的回滚段中取出最新的 undo-log 版本号,然后继续迭代下去,在 undo-log 中一层层往下找,最终就会找到稳定的数据!

    扫描二维码关注公众号,回复: 8576526 查看本文章
  3. trx_id_first < trx_id_current < tax_id_last,同上。

对比READ COMMITEDREPEATABLE READ

READ COMMITED 读已提交,只能解决脏读问题,但是不能解决不可重复读问题,所以每次都能读取到最新一份数据快照。

REPEATABLE READ可重复读,解决了脏读和不可重复读的问题,所以能读到事务开始前时的数据版本。

  • 使得 READ COMMITED 级别能够保证, 只要是当前语句执行前已经提交的数据都是可见的。
  • 使得 REPEATABLE READ 级别能够保证, 只要是当前事务执行前已经提交的数据都是可见的。

总结

  1. 一般我们认为MVCC有下面几个特点:
    • 每行数据都存在一个版本,每次数据更新时都更新该版本
    • 修改时Copy出当前版本, 然后随意修改,各个事务之间无干扰
    • 保存时比较版本号,如果成功(commit),则覆盖原记录, 失败则放弃copy(rollback)
    • 就是每行都有版本号,保存时根据版本号决定是否成功,听起来含有乐观锁的味道, 因为这看起来正是,在提交的时候才能知道到底能否提交成功
  2. 而InnoDB实现MVCC的方式是:
    • 事务以排他锁的形式修改原始数据
    • 把修改前的数据存放于undo log,通过回滚指针与主数据关联
    • 修改成功(commit)啥都不做,失败则恢复undo log中的数据(rollback)
  3. 二者本质的区别是 InnoDB 使用了 拍他锁。
    • InnoDB 的实现算不上 MVCC,因为没有实现核心的多版本共存,undo log 中的内容只是串行化的结果,记录了多个事务的过程,不属于多版本共存。
    • 因为InnoDB使用的MVCC中结合了排他锁, 不是纯的MVCC, 所以第一类更新丢失是不会出现了, 一般说更新丢失都是指第二类丢失更新。

猜你喜欢

转载自www.cnblogs.com/paulwang92115/p/12189487.html