MVCC大揭秘

MVCC

如果对undolog、隐藏列、readView不太了解的,可以先了解一下再看本文。

mysql中对于并发问题的处理有很多手段,其中对于经典场景【读-写】,mysql中innoDB引擎采用了MVCC多版本控制的手段来解决读写问题。

undolog

首先mysql中有undolog的机制,undolog就是当有写数据进行操作时,mysql会在真正执行写操作前,将现有数据备份至undolog中,而且多次的修改还会被分多次,那么对于同一条数据就会产生多条undolog,并且每条日志之间进行链表式串联。如下图所示:
在这里插入图片描述

隐藏列

下面隐藏字段中事务ID就标识了当前数据由哪个事务ID进行“写”的。

隐藏字段含义
DB_TRX_ID(Database Transaction Identifier)数据库事务标识符最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID
DB_ROLL_PTR (Database Rollback Pointer)数据库回滚指针回滚指针,指向这条记录的上一个版本,用于配合 undo log,指向上一个版本
DB_ROW_ID(Database Row Identifier) 数据库行标识符隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段

快照读与当前读

MVCC正是利用了快照读这种特性,使得innodb引擎在做读-写时并不需要通过加锁的方式来控制并发请求,而是通过写时加行级锁,读时进行快照读(前提是读操作不主动加锁)的方式处理了并发读写问题。而下方的readView便是实现快照读的一种手段。

readView

readView可以理解为查询数据时产生的一个视图(若查询语句加锁则不会产生该视图)

readView的组成

  • creator_trx_id: 创建这个readView的事务ID(只有写操作才会分配,读事务中事务ID=0)
  • trx_ids: 表示生成readView的那一刻,当前的活跃事务ID集合(未提交的事务)。
  • up_limit_id: 最小的活跃的事务ID。
  • low_limit_id: 表示生成readView后,系统中下一个事务应该被分配的ID值。比如当前系统中最大的事务ID=9,那么low_limit_id=9+1=10,即便9可能不是活跃的事务ID.

readView的规则

我们举一个例子进行带入,来说明一下readView都有哪些判断逻辑。(先以READ COMMITED隔离级别举例)

  • Read Committed: 每次读取数据前都生成一个readView
  • Repeatable Read: 在事务开启后第一次查询时生成一个readView, 之后的查询都用当前的这个readView,若出现了对数据的update,会更新readView.

当前sku表中数据

idsku_idsku_nameversionDB_TRX_ID
11001棒球帽18

其中DB_TRX_ID为隐藏列

现在有两个事务T1(txid=10)、T2(txid=20),T1进行写数据,T2进行读数据。

当前事务为T1(txid=10)
begin;
update sku set sku_name = '鸭舌帽' where id = 1;
update sku set sku_name = '贝雷帽' where id = 1;
...

当前事务为T2(txid=20)
begin;
update order set status = 'DONE' where id = 'xxx';
...

那么此时sku表中id=1的这条数据的版本链如下图所示:
在这里插入图片描述
基于上述场景,现在有个新事务执行查询操作:

begin;
select sku_name from sku where id = 1; # 此时得到的结果是棒球帽;

步骤拆解如下:

  1. 执行select语句时先生成readView,该readView的trx_ids=[10, 20], up_limited_id = 10, low_limited_id = 21, creator_trx_id = 0;
  2. 从当前上述的版本链中开始找数据,最新的数据name=‘贝雷帽’,对应trx_id=10,在trx_ids的范围内,不符合,根据指针找更小版本。
  3. 下一个name=‘鸭舌帽’,对应trx_id也等于10,也不符合,继续找。
  4. 下一个name=‘棒球帽’,对应trx_id=8,不在活跃事务ID范围内且小于up_limited_id,那么这个版本是符合要求的,于是最终的结果就是【棒球帽】;

我们一起来看下规则

  • 当被访问版本对应的trx_id与readView中的creator_trx_id相等,说明当前读请求和写请求处在同一事务中,那么该版本数据可以被读取。
  • 当被访问数据的trx_id < readView中的up_limit_id,说明该数据对应事务是已提交的,那么也可以被访问。
  • 当被访问数据trx_id > readView中的low_limit_id,说明该版本是在当前事务生产readView之后生成的,那么该版本数据不可被读取。
  • 当被访问数据的 trx_id在 up_limit_id和low_limit_id之间,那就要看trx_id是否存在于 trx_ids数组内。存在,说明该事务是活跃的事务,不可访问。 不存在,说明事务已提交,可以访问。

那么由上述规则加上例子,我们可以清晰的了解到mvcc是如何通过readView+undolog+隐藏列处理事务的并发读写问题的。

下一篇:
对于RR和RC如何选择?是否可以直接使用RC?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值