学习
实践
活动
工具
TVP
写文章
专栏首页windealli一致性无锁读与MVCC、undo-log、Read-View
原创

一致性无锁读与MVCC、undo-log、Read-View

一致性无锁读

什么是一致性无锁读

一致性无锁读包含两层含义:

  • 一致性读
  • 无锁

一致性读

一致性读,即快照读。在InnoDB中,事务中的查询会基于某个时间点创建的快照返回结果集,而非查询数据库表空间中的当前数据。一致性读(MySQL官方文档)

一致性读与当前读相对立:

  • 一致性读: 基于快照返回结果集, 普通的select即使用一致性读。
  • 当前读:基于当前数据库中已提交的,select ... lock in share mode,select ... for update,insert,update,delete 语句使用当前读

一致性读说白了就是让一个事务中的普通读操作不受其他事务的影响(不同的隔离级别有所差异)

InnoDB中,在RR、RC隔离级别下,默认开启一致性读,但是其创建快照的时机不同。

  • 在RR隔离级别下: 事务中的第一次一致性读(时间点)会创建一个快照,然后这个事务中后续所有的一致性读都基于这个快照返回数据。(除非本事务自己修改了相关数据)
  • 在RC隔离级别下: 事务每一次一致性读,都会重置快照。

无锁的一致性读

InnoDB,普通的select读操作是不会对记录加锁的,否则就会产生比较大的性能开销。

InnoDB中通过快照(Read-View)、MVCC、undo-log来实现一致性无锁读

  • MVCC:通过为表增加隐藏列,用来展示记录被修改的情况,以及提供了回溯历史版本的入口。
  • Read-View: 程序中快照以read_view_t结构体对象的形式存在,里面记录的与创建快照的timepoint相关联的一些信息(如当时系统中出现过的最大事务ID,活跃的最小事务ID等)
  • undo-log:撤销日志,用于将记录回退到某个版本的语句

MVCC

什么MVCC

MVCC,即:Multi-Versioning Concurrency Control(多版本并发控制)

MVCC 是InnoDB在RC(Read-Commited)和RR(Read-Repeated)隔离级别下提高并发和支持Rollback的技术,它保存了被修改行的历史版本信息,结合undo-log形成历史版本链。

InnoDB可以使用MVCC实现以下两个目标:

  1. 提高并发: 通过历史版本链可以重建某个时间点的版本,从而无锁实现一致性读。
  2. 回滚: MVCC和undo-log可以提供事务回滚所需要的信息。

本文主要介绍如何基于MVCC实现一致性无锁读

MVCC的基本设计

MVCC为每张表增加了下面三个隐藏列:

  • DB_TRX_ID: 当前记录被insert或update的最后一个事务的事务ID。(delete在内部也被视为update)
  • DB_ROLL_PTR: 指向用于将当前记录回滚到上一个版本的undo-log的指针。可以用于重建历史版本。
  • DB_ROW_ID: 自增列。

快照 Read-View

一致性读中的快照,在程序中医Read-View对象的形式存在。源码链接

struct read_view_t{
	ulint		type;
	undo_no_t	undo_no;
	trx_id_t	low_limit_no;
	trx_id_t	low_limit_id; 
	trx_id_t	up_limit_id;
	ulint		n_trx_ids;
	trx_id_t*	trx_ids;
	trx_id_t	creator_trx_id;
	UT_LIST_NODE_T(read_view_t) view_list;
};

这里对几个关键的字段进行下解释说明:

  • low_limit_id: 创建Read-View时出现过的最大的事务ID+1。所以事务ID比low_limit_id大的事务对记录所做的更新,都不应该被当前Read-View可见。
  • up_limit_id: 活跃事务列表trx_ids中最小的事务ID,所有事务ID小于up_limit_id的事务所做的更新,对当前Read-View可见。
  • trx_ids: 创建当前Read-View时,还未提交的活跃事务的事务ID列表。
  • creator_trx_id: 创建当前Read-View的事务ID。

undo-log

undo-log是InnoDB中与单个读写事务关联的撤消日志记录的集合。

undo-log中包含了如何撤销事务对聚簇索引记录所做的修改的信息。

当InnoDB需要读取某行记录的旧版本时,可以顺着undo-log找到对应的历史版本。

undo-log可以分为两大类:

  • insert undo log: insert语句产生的undo log, 仅在事务回滚时需要,insert事务提交后即可删除对应日志。
  • update undo log: update和delete语句产生的undo log,用于事务回滚和一致性读。因此只有与之相关的所有一致性读的事务都提交了访客删除(purge线程)

题外话:insert undo log在事务提交后即可删除,可以推出InnoDB无法基于undo-log和MVCC解决幻读问题。 幻读问题在RR隔离级别下仍然可能出现。

基于MVCC、Read-View、Undo-log实现一致性无锁读

有了前面的基础知识,我们来看下如何基于MVCC、Read-View、undo-log实现一致性无锁读。

首先,undo-log中提供了rebuild记录历史版本的信息(SQL语句),而MVCC则提供了重建历史版本的入口(指向undo-log的指针)。

那么现在的问题就是,如何确定一致性读对应的历史版本。

因为MVCC和undo-log中的版本信息是以事务ID来表示的,所以问题其实就是转化为:确定哪些事务对记录行的修改对当前可见。

快照创建前已经提交的事务所做的修改对当前快照可见,快照创建后才提交的事务所做的修改,对当前快照不可见。

因此一致性读的重点就是将Read-View中几个与事务ID相关的重点字段与数据库当前表数据和undo-log中DB_TRX_ID列进行对比。

如果能够直接知道创建Read-View时哪些事务提交,哪些事务未提交,那么一切就会变得简单。然后并没有这样的直接渠道。 数据库中,很可能事务ID大的先提交,事务ID小的后提交。

在Read-View中我们维护了low_limit_idup_limit_idtrx_ids, 将他们与MVCC、undo-log中的DB_TRX_ID列对比,即可得到哪些事务在创建Read-View已提交,哪些不是。

  1. 如果DB_TRX_ID < up_limit_id, 则修改改记录的事务在创建Read-View之前就提交了。该版本对这个一致性读可见。
  2. 如果DB_TRX_ID > low_limit_id, 则修改改记录的事务在创建Read-View时事务还未开启(更不用谈提交了),因此改版本对这个一致性读不可见。
  3. 如果up_limit_id < DB_TRX_ID < low_limit_id, 只能说明事务在创建Read-View前已经开启,但是无法判断其是否提交。需要与活跃列表进一步对比。 3.1 DB_TRX_ID在活跃列表trx_ids中,即事务还在活跃(还未提交),因此其修改不可见。 3.2 DB_TRX_ID不在活跃列表trx_ids中,即事务已经开启且不活跃,那必然就是已经提交。因此其修可见。

参考文献

MySQL 官方文档——consistent read

MySQL 5.7 Reference Manual: Consistent Nonlocking Reads

MySQL 5.7 Reference Manual: InnoDB Multi-Versioning

MySQL中MVCC的正确打开方式(源码佐证)

附录-脑图

image.png

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • 科普 | 几个小案例帮你搞懂MVCC实现原理

    都知道事务的可重复读级别实现原理是使用 MVCC 实现的,那么你对 MVCC 的底层实现原理知道多少呢!面试高频点,你值得拥有。

    CloudBest
  • 【数据库】事务?隔离级别?LBCC?MVCC?

    之前对事务的了解仅限于知道要么全部执行,要么全部不执行,能背出 ACID 和隔离级别,知其然但不知其所以然,现在觉得非常有必要系统学一下,关于事务,关于 LBC...

    JuneBao
  • MySQL基础隔离性小结

    有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。

    WindCoder
  • 两程序员玩“锁”,一人抢救无效身亡

    一般喜欢放在数据库来讲(其实这两个概念是属于计算机的,不要被误导),就说mysql吧,悲观锁,主要是表锁,行锁还有间隙锁,叶锁,读锁,因为这些锁在被触发的时候势...

    IT大咖说
  • 一文读懂Innodb MVCC实现原理

    • 不可重复读:事物A同样的查询条件,查询多次,读出的数据不一样,不一样的侧重点在于 update和delete

    公众号-Java编程大本营
  • HBase行锁与MVCC分析

    四个部分分析: 案例场景 流程解析 0.94-0.96实现方案分析 模拟试验及分析 一、案例场景 转发微博 抱歉,此微博已被作者删除。查看帮助:http://...

    财主刀刀
  • MySQL 事务和 MVCC 机制

    了解事务之前,先来看看数据库为什么需要有事务,假设没有事务会有什么影响?假设我们有一个银行账户系统,表结构如下:

    Se7en258
  • MySQL 事务隔离

    提到事务,你肯定会想到 ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性),今天...

    星尘的一个朋友
  • MySQL 百问百答 之 事务隔离

    事务要保证一组数据库操作,要么全成功,要么全失败。MySQL MyISAM引擎不支持事务,InnoDB支持。

    用户7447819
  • MySQL事务(二)事务隔离的实现原理:一致性读

    今天我们来学习一下MySQL的事务隔离是如何实现的。如果你对事务以及事务隔离级别还不太了解的话,这里左转。

    lbyxiaolizi
  • MySQL - 多版本控制 MVCC 机制初探

    MySQL InnoDB 存储引擎,实现的是基于多版本的并发控制协议——MVCC,而不是基于锁的并发控制。

    小小工匠
  • 【眼见为实】自己动手实践理解数据库REPEATABLE READ &amp;&amp; Next-Key Lock

    撸码那些事
  • MySQL数据库的事务隔离和MVCC

    事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(...

    数据和云
  • 【眼见为实】自己动手实践理解数据库READ COMMITTED &amp;&amp; MVCC

    撸码那些事
  • 【眼见为实】自己动手实践理解数据库REPEATABLE READ && Next-Key Lock

    [REPEATABLE READ]隔离级别解决了不可重复读的问题,一个事务中多次读取不会出现不同的结果,保证了可重复读。 还是上一篇中模拟不可重复读的例子: 事...

    撸码那些事
  • Mysql学习笔记【事务篇】

    当数据库中一个事务A正在修改一个数据但是还未提交或者回滚时,另一个事务B 来读取了修改后的内容并且使用了,然后事务A进行了提交,此时就引起了脏读。

    Porco1Rosso
  • mysql学习笔记(二)事务隔离

    银行转账例子,你要给朋友转账100,并且卡里只有100元。在期间转账中还可以做一些列操作,比如余额查询、余额转出、余额转入等操作,如果业务中显示转账成功...

    虞大大
  • 看懂MVCC“快照”的工作原理,领导给我升职架构师了

    事务隔离级别提到,如果是可重复读,事务T启动时会创建一个视图read-view。 之后事务T执行期间,即使有其他事务修改了数据,事务T看到的仍然跟在启动时看到...

    JavaEdge
  • MySQL基础篇3 mysql的事务隔离

    MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoD...

    历久尝新

扫码关注腾讯云开发者

领取腾讯云代金券