MySQL MVCC 实现

MVCC(Multiversion Concurrency Control),即多版本并发控制技术。它使得大部分支持行锁的事务引擎,不再单纯的使用行锁进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读(实现了读写并发),大大提高了数据库的并发性能。

实现

MVCC 是通过保存数据在某个时间点的快照来实现的。不同存储引擎的的 MVCC 实现是不同的。典型的有乐观并发控制和悲观并发控制。

InnoDB 的 MVCC 是通过在每行记录的后面保存隐藏的列来实现的,其中最主要的两列,这两列分别保存了这个行的创建时间和删除时间。这里存储的并不是实际的时间值,而是系统版本号(可以理解为事务的 ID)。每开始一个新事务,系统版本号就会自动递增。事务开始的版本号会作为事务的 ID。

InnoDB 存储的最基本的 row 中包含了一些额外的存储信息。

  • DATA_TRX_ID: 6字节,记录了更新这行的 transaction id。
  • DATE_ROLL_PTR:7字节,指向当前记录项的 rollback segment 和 undo log,找之前版本的的数据就是通过这个指针。
  • DB_ROW_ID:6字节,当由 InnoDB 自动产生聚集索引时,聚集索引包括这个 DB_ROW_ID 的值,否则不包含。
  • DELETE_BIT:1位,用于标记该记录是否被删除。这里不是真正的删除数据,而是标记出来的删除。

具体的执行过程
开始 -> 用排他锁锁定该行 -> 记录 redo log -> 记录 undo log -> 修改当前行的值,写事务编号,回滚指针指向 undo log 中的的修改前的行。

undo log 分 insert 和 update 两种,insert 因为没有原始数据,回滚时把 insert undo log 丢掉即可,而 update undo log 则必须遵守上述过程。

下面分别来说明

SELECT

InnoDB 检查每行的数据,确保他们符合两个标准:

  • “创建时间”早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者由当前事务创建和修改的行。
  • “删除时间“是未定义的或者大于当前事务的版本号,确定了当前事务开始之前,行没有被删除。

复合以上两点则返回查询结果。
“创建时间”,“删除时间”都不会被修改。

INSERT

InnoDB 为每个新增行记录当前系统版本号作为创建 ID。
即 “创建时间” = DB_ROW_ID

DELETE

InnoDB 为每个删除行的记录当前系统版本号作为行的删除 ID。
“创建时间”不变,删除时间=DB_ROW_ID

UPDATE

InnoDB 复制一行。这个新行的版本号使用系统版本号。他也把系统版本号作为了删除行的版本。
复制新增行的“创建时间” = DB_ROW_ID,删除时间未定义
旧数据行“创建时间”不变,“删除时间“=DB_ROW_ID

快照读和当前读

快照读:读取的是快照版本,也就是历史版本
当前读:读取的是最新版本

普通的 SELECT 就是快照读,而 UPDATE,DELETE,INSERT,SELECT … LOCK IN SHARE MODE,SELECT … FOR UPDATE 是当前读。

参考
连mysql锁的机制都不了解,怎么做架构师
MySQL事务隔离级别的实现原理