深入理解 MySQL 的 MVCC 机制

undo log

undo log是回滚日志,有两个作用:提供回滚操作多个行版本控制(MVCC)

在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败或回滚了,可以借助该undo进行回滚。

undo log和redo log记录物理日志不一样。undo log主要记录的是数据的逻辑变化,它是逻辑日志。

可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。

当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。

多行版本控制(MVCC)的时候,也是通过undo log来实现的:

当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

MySQL 事务隔离级别

除了redo log 和undo log 以外,另一个你在学习MVCC 之前不得不了解的知识,就是隔离级别。隔离级别是相当基础的知识,所以这里我们快速复习下就好。

 

注:MySQL 的默认隔离级别为repeatable-read级别

并且在MySQL中 repeatable-read级别还可以处理幻读,这是 MySQL独有的 next-keylock 实现的。

不同的数据库厂商对 SQL标准 中规定的四种隔离级别支持不一样:

Oracle 就只支持 read-committed 和 serializable 隔离级别。

MVCC 只在read-committed和repeatable-read 两个隔离级别下工作,其他两个隔离级别:

read-uncommitted,总是读取最新的数据行,而不会读当前事务版本的数据行。

serializable,则会对所有读取的行都加锁, 和 MVCC不兼容。

MVCC原理

版本链

MySQL的每行记录逻辑上其实是一个链表。

MySQL行记录中除了记录业务数据外,还有隐藏的 trx_id 和 roll_ptr

  • trx_id:表示最近修改的事务的id ,每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列。新增一个事务时,trx_id会递增,因此 trx_id 能够表示事务开始的先后顺序。
  • roll_pointer:指向该行上一个版本的地址,每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
update user set name = '源宝 2 ' where id = 1 

 

当然,这个链表存在于 undo log 中,和最新版本的数据不在一起。

每次更新后,都会将旧值放到一条 undo log 中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_ptr 属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。

另外,每个版本中还包含生成该版本时对应的 事务id(trx_id),这个信息很重要,我们稍后就会用到。

ReadView

说完了undo log,再来说说 ReadView。前面我们说过:MVCC 只在 read-committed 和 repeatable-read两个隔离级别下工作,而 read-committed 和 repeatable-read 的区别就在于它们生成 ReadView的策略不同。

对于使用 read-committed 和 repeatable-read 隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的。

核心问题就是:需要判断一下,版本链中的哪个版本是当前事务可见的

为此,InnoDB提出了一个ReadView的概念,这个ReadView 中有个 id 列表 trx_ids 来存储系统中当前活跃着的读写事务,也就是 begin 了还未 commit 或 rollback 的事务。

继续用之前的例子来理解一下 ReadView 和 trx_ids。

提交trx_id是2的记录后,接着有一个trx_id为3的事务,修改 name为源宝3,但是事务还没提交。

update user set name = '源宝 3 ' where id = 1 

则此时的版本链是︰

显然,此时的 trx_ids 为 [ 3 ]

 

如果另一个事务查询id为1的记录,因为trx_ids当前只有事务 trx_id 为 3 的事务,而 trx_ids 的意义是记录未完成的事务。在这里,事务未完成,所以该条记录不可见,继续查询下—条,结果返回源宝2

这时我把 trx_id 为 3 的事务提交了,并且新建了一个 trx_id 为 4 也修改 id 为 1 的记录name=源宝4,并且不提交事务。

update user set name = '源宝 4 ' where id = 1 

这时候版本链就是︰

 

read-committed —— 每次查询数据前都生成一个 ReadView

trx_ids 将更新为[ 4 ],版本链通过 trx_id 对比查找到的结果就是源宝3

repeatable-read —— 在第一次查询数据时生成一个 ReadView,之后的读都复用之前的。

不会有重建的 ReadView , trx_ids 还是 [ 3 ],MySQL 认为事务3未完成,所以 select 的结果是源宝2。第2次 select 结果和第1次一样,所以叫可重复读。

本文参考自深入理解 MySQL 的 MVCC 机制 - 云+社区 - 腾讯云 (tencent.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQLMVCC(Multi-Version Concurrency Control)机制是一种并发控制机制,用于处理并发事务的读写冲突。它通过为每个事务创建一个独立的版本,并使用这些版本来提供对数据的一致性读取,从而实现并发控制。 MVCC机制MySQL中的实现主要依赖于以下两个重要的组件: 1. Undo日志:MySQL使用undo日志记录数据修改操作的旧值。当一个事务开始时,MySQL会将当前数据行的快照复制到undo日志中。如果其他事务需要读取该数据行,它将读取这个快照而不受正在进行的事务的影响。 2. Read View:Read View是一个事务的快照视图,用于确定哪些数据行是对当前事务可见的。每个事务在开始时都会创建一个Read View。Read View包含一个活动事务列表和一个已提交事务列表。活动事务列表包含当前正在运行的活动事务,已提交事务列表包含已经提交的事务。当一个事务需要读取数据时,它会根据Read View确定哪些数据行是可见的。 基于这两个组件,MVCC机制提供了以下几个特点: 1. 高并发性:MVCC机制允许多个事务并发地读取和修改数据,因为它们之间不会产生读写冲突。 2. 一致性读取:MVCC机制确保事务只能读取已经提交的数据,避免了脏读和不可重复读的问题。 3. 无锁读取:MVCC机制的读取操作不会阻塞写入操作。读取操作只需要根据Read View判断数据是否可见。 需要注意的是,MVCC机制只适用于InnoDB存储引擎,而不适用于其他存储引擎,如MyISAM。另外,MVCC机制在一些特殊情况下可能会导致存储空间的增加,因为每个事务都会创建一个版本。因此,在设计数据库时需要考虑这些因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值