为何要避免长事务

仅用于自己理解MVCC的概念

先介绍几个MySQL相关的概念

read-view这个概念在很多的文章中都出现过,特别是在讲MVCC,一致性读概念的时候,但是在MySQL的官方文档中确没有发现(待更新),准确的说是Reference Manual中没有发现。唯一在官方些的文档中发现read-view这个字眼是MySQL的源码文档https://dev.mysql.com/doc/dev/mysql-server/9.0.1/classReadView.html#details

Read view lists the trx ids of those transactions for which a consistent read should not see the modifications to the database.

也就是说,我列出了一些修改记录的事务的ID,你一致性读就别想读这些内容了

看下面源码的注释,大家在不同文章中说的 consistent read viewconsistent snapshot一致性读视图其实说的都是一个东西,这里就以read-view 称呼

struct read_view_struct{
	ulint		type;	/*!< VIEW_NORMAL, VIEW_HIGH_GRANULARITY */
	undo_no_t	undo_no;/*!< 0 or if type is
				VIEW_HIGH_GRANULARITY
				transaction undo_no when this high-granularity
				consistent read view was created */

如下是read-view包含的一些字段

 private:
  /** The read should not see any transaction with trx id >= this
  value. In other words, this is the "high water mark". */
  trx_id_t m_low_limit_id;
 
  /** The read should see all trx ids which are strictly
  smaller (<) than this value.  In other words, this is the
  low water mark". */
  trx_id_t m_up_limit_id;
 
  /** trx id of creating transaction, set to TRX_ID_MAX for free
  views. */
  trx_id_t m_creator_trx_id;
 
  /** Set of RW transactions that was active when this snapshot
  was taken */
  ids_t m_ids;
 
  /** The view does not need to see the undo logs for transactions
  whose transaction number is strictly smaller (<) than this value:
  they can be removed in purge if not needed by other views */
  trx_id_t m_low_limit_no;

m_ids:快照开始时活跃事务的ID列表;
m_up_limit_id:如果事务ID小于这个值的话,那么当前事务是可以看到的。up_limit_id也就是这批活跃事务列表的最小值;
m_low_limit_id:如果事务ID大于等于这个值的话,那么当前事务都看不到。low_limit_id也就是这批活跃事务列表的最大值 + 1;

还有一个相关的id需要介绍DB_TRX_ID,MySQL官网在14.3 InnoDB Multi-Versioning这一节是这么介绍它的

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.

也就是说用来标记最后一个修改记录的事务ID

那么这个read-view是何时生成的?这里直接引用极客时间MySQL实战45讲里的一段话

begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。
第一种启动方式,一致性视图是在执行第一个快照读语句时创建的;
第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。

概念的准备工作基本完成

假设,现在有个事务启动了(MySQL默认隔离级别,RR),而且使用的是start transaction with consistent snapshot,那么一开始就会有这个read-view的创建。

假设现在活跃的事务列表为[20,25, 30]

现在这个事务要开始查询某个记录

1、假设现在记录的DB_TRX_ID为10,10 < 20,那么表示记录在read-view创建前就已经更新了,可以访问,人家都提交了,都比现在的活跃事务列表小了

2、假设现在记录的DB_TRX_ID为40,40 > 30 + 1,那么表示是在read-view创建后才更新了,当然不可以访问。那我现在就想读取数据怎么办呢?现在这个版本是我不能读的(有那么一点MVCC多版本的味道了),这时候就得去undo log拿了,为何去undo log拿?

If the row was updated, the undo log record contains the information necessary to rebuild the content of the row before it was updated.

不同的事务在启动的时候,我拿到的read-view是不同的,也就是我能看到的内容是不同的,一个值可能被从1改到了10。我启动的时候可能当时就是3,但是人家其他事务改了,我是看不到的,我就能看老的。那老的怎么看呢?就得靠undo log了,毕竟人家人家特性摆在那里呢

接着上面“DB_TRX_ID为40”继续,最新的不行,那我就要去undo log取旧的。怎么取呢?和刚才的步骤其实是一样的。在记录每次被修改的时候,是有字段记录了是谁修改了这个记录,也就是修改的事务ID,一直比较,直到最后找到

3、假设现在记录的DB_TRX_ID,m_up_limit_id <= DB_TRX_ID < low_limit_id` ,也就是DB_TRX_ID位于活跃事务列表的区间。如果此时活跃列表真的有DB_TRX_ID,那么还是不可见的,毕竟你还在活跃嘛,如果不在的话,那可以的,毕竟提交了嘛

回到最初的问题,为何长事务要避免。上面的read-view,在读取适合自己数据的时候,是用了undo log的,但是undo log不能一直留着啊,那么什么时候就不用了呢?这里也取MySQL实战45讲里评论举的一个例子:

事务A开启事务,获取read-view-A,执行查询row1 column1,不提交
事务B开启事务,修改row1记录,假设由column1 a -> b,此时记录undo 日志,提交;
事务C开启事务,修改row1记录,假设由column1 b -> c,此时记录undo 日志,提交;

此时事务A读取记录怎么读呢?既然维护了read-view,那么久还不提交,那么在读column1的时候,只能是逐级找undo log了,undo log 也没办法,只能给他留着这些玩意。还是MySQL实战45讲里提到的一个例子,20G的数据,回滚段能到200G。

所以,何时可以删除呢?就是事务A提交了,read-view-A不用再去费劲用这些维护的undo log了

参考

https://dev.mysql.com/doc/refman/5.7/en/innodb-multi-versioning.html
https://dev.mysql.com/doc/dev/mysql-server/9.0.1/classReadView.html#details
https://dev.mysql.com/doc/dev/mysql-server/9.0.1/read0types_8h_source.html
https://github.com/twitter-forks/mysql/blob/master/storage/innobase/include/read0read.h
https://time.geekbang.org/column/article/70562

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值