乐趣区

关于mysql:多版本并发控制-MVCC

介绍多版本并发管制

多版本并发控制技术(Multiversion Concurrency Control,MVCC)

技术是为了解决问题而生的,通过 MVCC 咱们能够解决以下几个问题:

  1. 读写之间阻塞的问题:通过 MVCC 能够让读写相互不阻塞,即读不阻塞写,写不阻塞读,这样就能够晋升事务并发解决能力。
  2. 升高了死锁的概率:这是因为 MVCC 没有应用锁,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。
  3. 解决一致性读的问题:一致性读也被称为快照读,当咱们查询数据库在某个工夫点的快照时,只能看到这个工夫点之前事务提交更新的后果,而不能看到这个工夫点之后事务提交更新的后果。

MVCC 的思维

MVCC 是通过数据行的历史版本来实现数据库的并发管制。

简略来说 MVCC 的思维就是保留数据的历史版本。这样一个事务进行查问操作时,就能够通过比拟版本号来判断哪个较新的版本对以后事务可见。

InnoDB 对 MVCC 的实现

MVCC 没有正式的规范,所以在不同的 DBMS 中,MVCC 的实现形式可能是不同的。

InnoDB 对 MVCC 的实现次要是通过 版本链 + ReadView 构造实现。

版本链存储记录的多个版本

先介绍聚簇索引记录的暗藏列,再介绍 Undo Log 版本链


对于应用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都蕴含 3 个暗藏列

  1. db_row_id:暗藏的行 ID。在没有自定义主键也没有 Unique 键的状况下,会应用该暗藏列作为主键。
  2. db_trx_id:操作这个数据的事务 ID,也就是最初一个对该数据进行插入或更新的事务 ID。
  3. db_roll_ptr:回滚指针,也就是指向这个记录的 Undo Log 信息。Undo Log 中存储了回滚须要的数据。

事务 ID

事务执行过程中,只有在第一次真正批改记录时(比方进行 insert、delete、update 操作),才会被调配一个惟一的、枯燥递增的事务 ID,如果没有批改记录操作,依照肯定的策略调配一个比拟大的事务 ID,缩小调配事务 ID 的锁竞争。每当事务向数据库写入新内容时,所写的数据都会被标记操作所属的事务的事务 ID。


在 InnoDB 存储引擎中,版本链由数据行的 Undo Log 组成。

每次对数据行进行批改,都会将旧值记录到 Undo Log,算是该数据行的一个旧版本。

Undo Log 有两个重要的属性:db_roll_ptr、db_trx_id

  • Undo Log 也有一个 db_roll_ptr 属性(insert 操作对应的 Undo Log 没有 db_roll_ptr 属性,因为 insert 操作对应的数据行没有更早的版本),Undo Log 的 db_roll_ptr 属性指向上一次操作的 Undo Log,所有的版本被 db_roll_ptr 属性连贯造成一个链表。该链表即版本链,版本链的头节点就是数据行的最新值。
  • Undo Log 还蕴含生成该版本时,对应的事务 ID,用于判断以后版本的数据对事务的可见性。

版本链如下图所示。这样如果咱们想要查找历史快照,就能够通过遍历回滚指针的形式进行查找。

ReadView 判断版本链中的哪个较新的版本对以后事务是可见的

ReadView 用来判断版本链中的哪个较新的版本对以后事务是可见的。

ReadView 中次要蕴含 4 个比拟重要的属性:

  • m_ids:示意在生成 ReadView 时,以后零碎中所有沉闷的读写事务的 ID 汇合(列表)
  • min_transaction_id:示意在生成 ReadView 时,m_ids 中的最小值
  • max_transaction_id:示意在生成 ReadView 时,零碎应该调配给下一个事务的 ID 值
  • creator_transaction_id:示意生成该 ReadView 的事务的 ID

有了这个 ReadView,这样在拜访某条记录时,就能够用 ReadView 来判断版本链中的哪个较新的版本对以后事务是可见的。

  • 如果被拜访版本的 transaction_id 属性值与 ReadView 中的 creator_trx_id 值雷同,表明以后事务在拜访它本人批改过的记录,所以该版本能够被以后事务拜访。
  • 如果被拜访版本的 transaction_id 属性值 小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在以后事务生成 ReadView 前曾经提交了,所以该版本能够被以后事务拜访。
  • 如果被拜访版本的 transaction_id 属性值 大于 ReadView 中的 max_trx_id 值,表明生成该版本的事务在以后事务生成 ReadView 后才开启,所以该版本不能够被以后事务拜访。
  • 如果被拜访版本的 transaction_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就须要判断一下 transaction_id 属性值是不是在 m_ids 列表中:

    • 如果在,表明生成 ReadView 时,被拜访版本的事务还是沉闷的,所以该版本不能够被以后事务拜访
    • 如果不在,表明生成 ReadView 时,被拜访版本的事务曾经被提交了,所以该版本能够被以后事务拜访

如果某个版本的数据对以后事务不可见的话,那就顺着版本链找到下一个版本的数据,持续依照上边的步骤判断

可见性,依此类推,直到版本链中的最初一个版本。如果最初一个版本也不可见的话,那么就意味着该条记录对以后事务齐全不可见,查问后果就不蕴含该记录。

ReadView 的生成机会

MVCC 能够避免脏读,也能够避免不可反复读。

避免脏读 和 避免不可反复读 实现的不同之处就在:ReadView 的生成机会不同

  • 避免脏读:每次读取数据前,都生成一个 ReadView
  • 避免不可反复读:在以后事务第一次读取数据时,生成一个 ReadView,之后的查问操作都重复使用这个 ReadView

对于隔离级别为 读未提交 的事务来说,间接读取记录的最新版本即可。

对于隔离级别为 串行化 的事务来说,InnoDB 存储引擎应用加锁的形式来拜访记录。

对于隔离级别为 读已提交 和 可反复读 的事务来说,都必须保障只能读到曾经提交的事务批改的数据,不能读到未提交的事务批改的数据。

参考资料

MySQL 是怎么运行的:从根儿上了解 MySQL – 小孩子 4919 – 掘金课程 (juejin.cn)

退出移动版