innoDB 的多版本并发控制(MVCC)

MVCC全称Mutli Version Concurreny Control,多版本并发控制,也可称之为一致性非锁定读;它通过行的多版本控制方式来读取当前执行时间数据库中的行数据。实质上使用的是快照数据,这样就可以实现不加锁读。MVCC 主要应用于 Read Commited 和 Repeatable read 两个事务隔离级别。

MVCC的实现

innoDB 向数据库中存储的每行添加三个隐藏字段

mysql事务是由单独线程处理的吗 mysql实现事务的原理(mvcc)_数据库

 1 DB_TRX_ID 事务id
占6 字节,表示这一行数据最后插入或修改的事务id。此外删除在内部也被当作一次更新,在行的特殊位置添加一个删除标记(记录头信息有一个字节存储是否删除的标记)。
DB_ROLL_PTR 回滚指针
占7字节,回滚指针指向被写在Rollback segment中的undoLog记录,在该行数据被更新的时候,undoLog 会记录该行修改前内容到undoLog。
DB_ROW_ID 行ID
占7字节,他就项自增主键一样随着插入新数据自增。如果表中不存主键 或者 唯一索引,那么数据库 就会采用DB_ROW_ID生成聚簇索引。否则DB_ROW_ID不会出现在索引中。

3.2 undo log

undo log是为回滚而用,具体内容就是copy事务前的数据库内容(行)到undo buffer,在适合的时间把undo buffer中的内容刷新到磁盘。undo buffer与redo buffer一样,也是环形缓冲,但当缓冲满的时候,undo buffer中的内容会也会被刷新到磁盘;与redo log不同的是,磁盘上不存在单独的undo log文件,所有的undo log均存放在主ibd数据文件中(表空间),即使客户端设置了每表一个数据文件也是如此。

undo log 在 Rollback segment中又被细分为 insert 和 update undo log , insert 类型的undo log 仅仅用于事务回滚,当事务一旦提交,insert undo log 就会被丢弃。update的undo log 被用于 一致性的读和事务回滚,update undo log 的清理 是在 没有事务 需要对这部分数据快照进行一致性读的时候 进行清理, (重点说明:innoDB 在update时会为记录上锁,因此保证了多个事物的回滚段数据一定时有序的)。

undo log 的创建

每次对数据进行更新操作时,都会copy 当前数据,保存到undo log 中。并修改 当前行的 回滚指针指向 undo log 中的 旧数据行。

mysql事务是由单独线程处理的吗 mysql实现事务的原理(mvcc)_数据库_02

3.3 read_view 判断数据行可见性
在innodb中,创建一个新事务的时候,innodb会将当前系统中的活跃事务列表创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。当用户在这个事务中要读取该行记录的时候,innodb会将该行当前的版本号与该read view进行比较。
具体的算法是(可重复读级别):
假设当前 数据行 事务ID 为 T0 ,read view 中保存的 最老的事务id T_min ,最新的 事务id 为 T_max,当前进行的事务id 为 T_new 。

  • 如果 T0 < T_min ,那么该行数据可见。
    因为 T0 在 T_new 事务开始前 已经提交。
  • 如果 T0 > T_max ,数据行不可见。根据DB_ROLL_PTR 指针 找到下一个 数据版本,再次进行数据可见性判断。
    因为 T0事务 在 T_new 开始前并不存在,也就是说T0 在T_new 开始后 创建。
  • 如果 T_min <= T0 <= T_max ,判断T0 是否在read_view 中,如果 不在该行数据可见。如果不可见根据DB_ROLL_PTR 指针 找到下一个 数据版本,再次进行数据可见性判断。

总之就是只能显示在当前事物中提交的数据和在当前事物之前提交的最新数据,其它事物未提交的数据无法显示

3.4 Read Commited ,Repeatable read 数据可见性判断
Read Commited 和 Repeatable read 采用相同的数据可见性判断逻辑。
那么怎么在相同的判断逻辑下 分别 实现 RC 和 RR 级别的?

  • Read Commited
    在每次语句执行的过程中,都关闭read_view, 重新创建当前的一份新的read_view。
    这样就可以根据当前的全局事务链表创建read_view的事务区间,实现read committed隔离级别。
  • Repeatable read
    在repeatable read的隔离级别下,创建事务trx结构的时候,就生成了当前的global read view。
    使用trx_assign_read_view函数创建,一直维持到事务结束,这样就实现了repeatable read隔离级别。

正是因为Read Commited和 Repeatable read的read view 生成方式和时机不同,导致在不同隔离级别下,read committed 总是读最新一份快照数据,而repeatable read 读事务开始时的行数据版本,因为repeatable read 维护的时全局的read_view,因此比较的时间可能会更长,因此性能要稍逊于Read Commited

Read unCommited 没有进行版本控制,事物只进行回滚控制,永远都是查询表中的数据,不会进行版本比较,性能最快但是无法满足商用。


Serializable:是在Repeatable read的基础上为每次查询都加锁, for update,因此性能比较慢不建议使用.


 mysql 默认的事物隔离级别为 Repeatable read

oracle 默认的事物隔离级别为 Read Commited