MVCC中Row的可见性问题解析

栏目: 数据库 · 发布时间: 5年前

内容简介:我们都知道MVCC通过快照读不加锁、读和读、读和写之间互不影响来提高数据库并发能力,但是它是如何管理不同的事务更新同一行数据时产生的不同版本和新事务的查询需求读取哪个版本的数据之间的关系的(即哪个版本对这个事务是可见,可以返回的)?要想明白这个问题,需要先搞清楚什么是「行记录的可见性问题」。InnoDB支持MVCC多版本,其中RC(Read Committed)和RR(Repeatable Read)隔离级别是利用consistent read view(一致读视图)方式支持的。 所谓consistent

我们都知道MVCC通过快照读不加锁、读和读、读和写之间互不影响来提高数据库并发能力,但是它是如何管理不同的事务更新同一行数据时产生的不同版本和新事务的查询需求读取哪个版本的数据之间的关系的(即哪个版本对这个事务是可见,可以返回的)?要想明白这个问题,需要先搞清楚什么是「行记录的可见性问题」。

MVCC解决的问题

InnoDB支持MVCC多版本,其中RC(Read Committed)和RR(Repeatable Read)隔离级别是利用consistent read view(一致读视图)方式支持的。 所谓consistent read view就是在某一时刻给事务系统trx_sys打snapshot(快照),把当时trx_sys状态(包括活跃读写事务数组)记下来,之后的所有读操作根据其事务ID(即trx_id)与snapshot中的trx_sys的状态作比较,以此判断read view对于事务的可见性。

Row的记录格式

先来看看 MySQL 官方手册的 介绍

Internally, InnoDB adds three fields to each row stored in the database.  A 6-byte DB_TRX_ID field indicates the transaction identifier for the last transaction that inserted or updated the row. Also, a deletion is treated internally as an update where a special bit in the row is set to mark it as deleted. Each row also contains a 7-byte DB_ROLL_PTR field called the roll pointer. The roll pointer points to an undo log record written to the rollback segment. If the row was updated, the undo log record contains the information necessary to rebuild the content of the row before it was updated. A 6-byte DB_ROW_ID field contains a row ID that increases monotonically as new rows are inserted. If InnoDB generates a clustered index automatically, the index contains row ID values. Otherwise, the DB_ROW_ID column does not appear in any index.

上面这段就是说InnoDB存储引擎在每行记录上存有以下几个字段:

  • DB_TRX_ID,标识最后一个事务的更新和插入
  • DB_ROLL_PTR,事务回滚指针,指向当前记录项的undo log信息
  • DB_ROW_ID,标识插入的新的数据行的ID
  • DELETED,删除标记位

Row记录的可见性实现

Read view中保存的trx_sys状态主要包括:

  • low_limit_id:high water mark,大于等于view->low_limit_id的事务对于view都是不可见的
  • up_limit_id:low water mark,小于view->up_limit_id的事务对于view一定是可见的
  • low_limit_no:trx_no小于view->low_limit_no的undo log对于view是可以purge的
  • rw_trx_ids:读写事务数组

RR隔离级别(除了Gap锁之外)和RC隔离级别的差别是创建snapshot时机不同。 RR隔离级别是在事务开始时刻,确切地说是第一个读操作创建read view的;RC隔离级别是在语句开始时刻创建read view的,并且每条语句都创建一次read view。

History List是什么

记录的DB_ROLL_PTR指向最近一次更新所创建的回滚段;每条undo log也会指向更早版本的undo log,从而形成一条更新链。通过这个更新链,不同事务可以找到其对应版本的undo log,组成old version记录,这条链就是记录的history list。 生成read_view: 每个事务在开始的时候都会根据当前系统的活跃事务链表创建一个read_view。 比如连续的三个事务,第一个是insert,后两个分别更新了同一个字段,那么对于trx3来说它的 DB_ROLL_PTR 指向了trx2更新完毕时的记录,对于trx2 来说它的 DB_ROLL_PTR 指向了trx1插入时的那条记录,所以从trx3的 DB_ROLL_PTR 开始是可以找到一条单链表的。

活跃事务和活跃事务列表是什么

活跃事务就是还没有 commit 的事务,而活跃事务列表简单可以理解为Read View,并不意味着已经提交的事务一定比这些列表里的事务的 DB_TRX_ID 小,这完全取决于事务的执行复杂程度。

举个例子

readview = 活跃事务列表
readview(RR): 事务开始时产生readview
readview(RC): 每条语句都会产生readview

如何判断可见性:

假设:活跃事务列表为(3,4,5,6)即readview,当前事务id号为10,修改这条行记录后, 这条记录上的db_trx_id=10

流程如下:
当前事务(trxid=10)执行完第一个语句后生成了一个最新的readview(注:括号中的是事务id): 
(3[min_trx_id],4,5,6[max_trx_id]),然后以此为参考去查看当前事务要操作(读取、更新)的记录的Row中的属性:
1. 如果该row上的db_trx_id in (`活跃事务列表`),那么说明此记录还未提交,这条记录对于此事务不可见,
需要调用上一个undo,用同样的判断标准过滤,依次推进,直到找到db_trx_id小于min_trx_id的记录
2. 如果该row上的db_trx_id < `活跃事务列表最小值`,那么说明已经提交,这条记录对于此事务可见
3. 如果该row上的db_trx_id > `活跃事务列表最大值`, 那么说明该记录在当前事务之后提交,这条记录对于
此事务不可见,需要调用上一个undo,用同样的判断标准过滤,依次推进,直到找到db_trx_id小于min_trx_id的记录

快速问答

  • Q1. 是不是一个事务的ID比当前事务的ID小就是可以被当前事务读取到的?
  • A:不是,首先它可能是活跃的,活跃的事务肯定是不可以被其他事务看见的,不管这两个事务之间的事务ID的大小关系如何。其次,它是非活跃事务也不一定一定对大于它的事务ID 的事务可见,需要保证它的事务ID一定比当前Read View中的min_trx_id小才可以,说明它已经提交了。只有它比Read View中的min_trx_id值小时候

  • Q2. 是不是一个事务的ID比当前事务ID大的时候,一定不可以被当前事务读取到?
  • A:是的,这个是规定,后开启的事务对之前开启的事务是不可见的。

  • Q3. 当前事务id更新某行后,会锁住该行记录并更新db_trx_id,如10,那么该记录上的trx_id肯定是<=当前事务id(10)的,那既然这样,怎么会产生db_trx_id > 活跃事务列表最大值的行呢?
  • A:因为当前事务不仅仅是读取这条被锁住的记录,可能还需要读取其他记录(这些记录当然可能被其他更靠后的事务id更新了),那么这时候其他记录上的db_trx_id大于当前记录的db_trx_id 就很正常不过了。

创建readview的位置,不是begin的那个位置,而是begin后面的 SQL 语句的位置。(通过验证)

如果你想在RR级别下开启transaction的时候就产生readview,分配事务id,那么可以这样操作: start transaction with consistent snapshot (并未验证过)

References

  • https://keithlan.github.io/2017/06/16/innodb_locks_MVCC/
  • https://blog.csdn.net/endlu/article/details/51518377
  • https://yq.aliyun.com/articles/560506

本文首次发布于ElseF’s Blog, 作者 @stuartlau , 转载请保留原文链接.


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

旷世之战――IBM深蓝夺冠之路

旷世之战――IBM深蓝夺冠之路

纽伯 / 邵谦谦 / 清华大学出版社 / 2004-5 / 35.0

本书作者Monty Neworn是国际计算机象棋协公的主席,作者是用生动活泼的笔触描写了深蓝与卡斯帕罗夫之战这一引起全世界关注的历史事件的前前后后。由于作者的特殊身份和多年来对计算机象棋的关心,使他掌握了许多局外人不能得到的资料,记叙了很多鲜为人知的故事。全书行文流畅、文笔优美,对于棋局的描述更是跌宕起伏、险象环生,让读者好像又一次亲身经历了那场流动人心的战争。 本书作为一本科普读物......一起来看看 《旷世之战――IBM深蓝夺冠之路》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具