MySql教程

MySQL-如何实现可重复读

本文主要是介绍MySQL-如何实现可重复读,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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

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

MVCC的实现

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

 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 的清理 是在 没有事务 需要对这部分数据快照进行一致性读的时候 进行清理。
undo log 的创建
每次对数据进行更新操作时,都会copy 当前数据,保存到undo log 中。并修改 当前行的 回滚指针指向 undo log 中的 旧数据行。

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 读事务开始时的行数据版本。

这篇关于MySQL-如何实现可重复读的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!