0

0

MySQL跨行事务模型(图文详解)

青灯夜游

青灯夜游

发布时间:2019-11-29 16:27:41

|

2872人浏览过

|

来源于cnblogs

转载

MySQL跨行事务模型(图文详解)

MySQL事务模型在网上也有很多的介绍,在写这篇文章之前本人也翻看了很多资料作为参考,以期让自己理解的更加深入全面。看了大多数介绍文章之后发现部分文章并不完整,比如有的只介绍了几种隔离级别下MySQL的表现,并没有从技术角度进行解读。有的文章说的倒很全面,但缺乏些许条理,读起来并不容易理解。这也是笔者希望能够带给大家一点不一样的东西,从技术角度进行解读,并且利于理解。

MySQL事务原子性保证

事务原子性要求事务中的一系列操作要么全部完成,要么不做任何操作,不能只做一半。原子性对于原子操作很容易实现,就像HBase中行级事务的原子性实现就比较简单。但对于多条语句组成的事务来说,如果事务执行过程中发生异常,需要保证原子性就只能回滚,回滚到事务开始前的状态,就像这个事务根本没有发生过一样。如何实现呢?

MySQL实现回滚操作完全依赖于undo log,多说一句,undo log在MySQL除了用来实现原子性保证之外,还用来实现MVCC,下文也会涉及到。使用undo实现原子性在操作任何数据之前,首先会将修改前的数据记录到undo log中,再进行实际修改。如果出现异常需要回滚,系统可以利用undo中的备份将数据恢复到事务开始之前的状态。下图是MySQL中表示事务的基本数据结构,其中与undo相关的字段为insert_undo和update_undo,分别指向本次事务所产生的undo log。

数据库事务系列-MySQL跨行事务模型

事务回滚根据update_undo(或者insert_undo)找到对应的undo log,做逆向操作即可。对于已经标记删除的数据清理删除标记,对于更新数据直接回滚更新;插入操作稍微复杂一些,不仅需要删除数据,还需要删除相关的聚集索引以及二级索引记录。

undo log是MySQL内核中非常重要的一块内容,涉及知识比较多而且复杂,比如:

1. undo log必须在数据修改之前持久化,undo log持久化需不需要记录redo以防止宕机异常?如果需要就又涉及宕机恢复…

2. 通过undo log如何实现MVCC?

3. 那些undo log可以在什么场景下回收清理?如何清理?

MySQL事务一致性保证:强一致性事务保证

MySQL事务隔离级别


Read Uncommitted(RU技术解读:使用X锁实现写写并发)

Read Uncommitted只实现了写写并发控制,并没有有效的读写并发控制,导致当前事务可能读到其他事务中还未提交的修改数据,这些数据准确性并不靠谱(有可能被回滚掉),因此在此基础上作出的一切假设就都不靠谱的。在现实场景中很少有业务会选择该隔离级别。

写写并发实现机制和HBase并无两样,都是使用两阶段锁协议对相应记录加行锁实现。不过MySQL中行锁机制比较复杂,根据行记录是否是主键索引、唯一索引、非唯一索引或者无索引等分为多种加锁情况。

1. 如果id列是主键索引,MySQL只会为聚簇索引记录加锁。

2. 如果id列是唯一二级索引,MySQL会为二级索引叶子节点以及聚簇索引记录加锁。

3. 如果id列是非唯一索引,MySQL会为所有满足条件(id = 15)的二级索引叶子节点以及对应的聚簇索引记录加锁。

4. 如果id列是无索引的,SQL会走聚簇索引全表扫描,并将扫描结果加载到SQL Server层进行过滤,因此InnoDB会为扫描过的所有记录先加上锁,如果SQL Server层过滤不符合条件,InnoDB会释放该锁。因此InnoDB会为扫描到的所有记录都加锁,很恐怖吧!

接下来无论是RC、RR,抑或是Serialization,写写并发控制都使用上述机制,所以不再赘述。接下来会重点分析RC和RR隔离级别中的读写并发控制机制。

在详细介绍RC和RR之前,有必要在此先行介绍MySQL中MVCC机制,因为RC和RR都使用MVCC机制实现事务之间的读写并发。只不过两者在实现细节上有一些区别,具体区别接下来再聊。

MVCC in MySQL

MySQL中MVCC机制相比HBase来说要复杂的多,涉及的数据结构也比较复杂。为了解释的比较清晰,以一个栗子为模版进行解释。比如当前有一行记录如下图所示:

数据库事务系列-MySQL跨行事务模型

前面四列是该行记录的实际列值,需要重点关注的是DB_TRX_ID和DB_ROLL_PTR两个隐藏列(对用户不可见)。其中DB_TRX_ID表示修改该行事务的事务ID,而DB_ROLL_PTR表示指向该行回滚段的指针,该行记录上所有版本数据,在undo中都通过链表形式组织,该值实际指向undo中该行的历史记录链表。

现在假设有一个事务trx2修改了该行数据,该行记录就会变为下图形式,DB_TRX_ID为最近修改该行事务的事务ID(trx2),DB_ROLL_PTR指向undo历史纪录链表:

数据库事务系列-MySQL跨行事务模型

了解了MySQL行记录之后,再来看看事务的基本结构,下图是MySQL的事务数据结构,上文我们提到过。事务在开启之后会创建一个数据结构存储事务相关信息、锁信息、undo log以及非常重要的read_view信息。

read_view保存了当前事务开启时整个MySQL中所有活跃事务列表,如下图所示,在当前事务开启的时候,系统中活跃的事务有trx4、trx6、trx7以及trx10。另外,up_trx_id表示当前事务启动时,当前事务链表中最小的事务ID;low_trx_id表示当前事务启动时,当前事务链表中最大的事务ID。

数据库事务系列-MySQL跨行事务模型

read_view是实现MVCC的一个关键点,它用来判断记录的哪个版本对当前事务可见。如果当前事务要读取某行记录,该行记录的版本号(事务ID)为trxid,那么:

1. 如果trxid

2. 如果trxid > low_trx_id,说明该行事务所在的事务是在当前事务创建之后才开启,所以该行记录对当前事务不可见。

3. 如果up_trx_id

以下面行记录为例,该行记录存在多个版本(trx2、trx5、trx7以及trx12),其中trx12是最新版本。看看该行记录中哪个版本对当前事务可见。

Destoon B2B网站
Destoon B2B网站

Destoon B2B网站管理系统是一套完善的B2B(电子商务)行业门户解决方案。系统基于PHP+MySQL开发,采用B/S架构,模板与程序分离,源码开放。模型化的开发思路,可扩展或删除任何功能;创新的缓存技术与数据库设计,可负载千万级别数据容量及访问。 系统特性1、跨平台。支持Linux/Unix/Windows服务器,支持Apache/IIS/Zeus等2、跨浏览器。基于最新Web标准构建,在

下载

1. 该行记录的最新版本为trx12,与当前事务read_view进行对比发现,trx12大于当前活跃事务列表中的最大事务trx10,表示trx12是在当前事务创建之后才开启的,因此不可见。

2. 再查看该行记录的第二个最新版本为trx7,与当前事务read_view对比发现,trx7介于当前活跃事务列表最小事务ID和最大事务ID之间,表明该行记录所在事务在当前事务创建的时候处于活动状态,在活跃列表中遍历发现trx7确实存在,说明该事务还没有提交,所以对当前事务不可见。

3. 继续查看该记录的第三个最新版本trx5,也介于当前活跃事务列表最小事务ID和最大事务ID之间,表明该行记录所在事务在当前事务创建的时候处于活动状态,但遍历发现该版本并不在活跃事务列表中,说明trx5对应事务已经提交(注:事务提交时间与事务编号没有任何关联,有可能事务编号大的事务先提交,事务编号小的事务后提交),因此trx5版本行记录对当前事务可见,直接返回。

数据库事务系列-MySQL跨行事务模型


Read Committed(技术解读:写写并发使用X锁,读写并发使用MVCC避免脏读)

上文介绍了MySQL中MVCC技术实现机制,但要明白RC隔离级别下事务可见性,还需要get一个核心点:RC隔离级别下的事务在每次执行select时都会生成一个最新的read_view代替原有的read_view。

数据库事务系列-MySQL跨行事务模型

如上图所示,左侧为1号事务,在不同时间点对id=1的记录分别查询了三次。右侧为2号事务,对id=1的记录进行了更新。更新前该记录只有一个版本,更新好变成了两个版本。

1号事务在RC隔离级别下每次执行select请求都会生成一个最新的read_view,前两次查询生成的全局事务活跃列表中包含trx2,因此根据MVCC规定查到的记录为老版本;最后一次查询的时间点位于2号事务提交之后,因此生成的全局活跃事务列表中不包含trx2,此时在根据MVCC规定查到的记录就是最新版本记录。

Repeatable Read(技术解读:写写并发使用X锁,读写并发使用MVCC避免不可重复读;当前读使用Gap锁避免幻读)

和RC模式不同,RR模式下事务不会再每次执行select的时候生成最新的read_view,而是在事务第一次select时就生成read_view,后续不会再变更,直至当前事务结束。这样可以有效避免不可重复读,使得当前事务在整个事务过程中读到的数据都保持一致。示意图如下所示:

数据库事务系列-MySQL跨行事务模型

这个就很容易理解,三次查询所使用的全局活跃事务列表都一样,且都是第一次生成的read_view,那之后查到的记录必然和第一次查到的记录一致。

RR隔离级别能够避免幻读吗?

如果对幻读还不了解的话,可以参考该系列的第一篇文章。如下图所示,1号事务对针对id>1的过滤条件执行了三次查询,2号事务执行了一次插入,插入的记录刚好符合id>1这个条件。可以看出来,三次查询得到的数据是一致的,这个是由RR隔离级别的MVCC机制保证的。这么看来,是避免了幻读,但是在最后1号事务在id=2处插入一条记录,MySQL会返回Duplicate entry的错误,可见避免了幻读是一种假象。

数据库事务系列-MySQL跨行事务模型

严格意义避免幻读(技术解读:当前读使用Gap锁避免幻读)

之前提到的所有RR级别的select语句我们称为快照读,快照读能够保证不可重复读,但并不能避免幻读。于是MySQL又提出”当前读”的概念,常见的当前读语句有:

1.  select for update

2.  select lock in share mode

3.  update / delete

并且规定,RR级别下当前读语句会给记录加上一种特殊的锁-Gap锁,Gap锁并不锁定某个具体的记录,而是锁定记录与记录之间的间隔,保证这个间隔中不会插入新的其他记录。下图是一个示意图:

数据库事务系列-MySQL跨行事务模型

上图中1号事务首先执行了一个当前读的select语句,这个语句会在 id > 0的所有间隔加上Gap锁,接下来2号事务在id = 3处执行插入时系统就会返回Lock wait timeout execcded的异常。当然,其他事务可以在id

Serializable (技术解读:S锁(读)+X锁(写))

Serialization隔离级别是最严格的隔离级别,所有读请求都会加上读锁,不分快照读和当前读,所有写会加上写锁。当然,这种隔离级别的性能因为锁开销而相对最差。

MySQL事务持久性保证

MySQL事务持久化策略和HBase基本相同,但是涉及的组件相对比较多,主要有doublewrite、redo log以及binlog:

1. MySQL数据持久化(DoubleWrite)

实际上MySQL的真实数据写入分为两次写入,一次写入到一个称为DoubleWrite的地方,写成功之后再真实写入数据所在磁盘。为什么要写两次?这是因为MySQL数据页大小与磁盘一次原子操作大小不一致,有可能会出现部分写入的情况,比如默认InnoDB数据页大小为16K,而磁盘一次原子写入大小为512字节(扇区大小),这样一个数据页写入需要多次IO,这样一旦中间发生异常就会出现数据丢失。另外需要注意的是DoubleWrite性能并不会影响太大,因为写入DoubleWrite是顺序写入,对性能影响来说不是很大。

2. redolog持久化策略(innodb_flush_log_at_trx_commit)

redolog是InnoDB的WAL,数据先写入redolog并落盘,再写入更新到bufferpool。redolog的持久化策略和HBase中hlog的持久化策略一致,默认为1,表示每次事务提交之后log就会持久化到磁盘;该值为0表示每隔1秒钟左右由异步线程持久化到磁盘,这种情况下MySQL发生宕机有可能会丢失部分数据。该值为2表示每次事务提交之后log会flush到操作系统缓冲区,再由操作系统异步flush到磁盘,这种情况下MySQL发生宕机不会丢失数据,但机器宕机有可能会丢失部分数据。

3. binlog持久化策略(sync_binlog)

binlog作为Server层的日志系统,主要以events的形式顺序纪录了数据库的各种操作,同时可以纪录每次操作所花费的时间。在MySQL官方文档上,主要介绍了Binlog的两个最基本核心作用:备份和复制,因此binlog的持久化会一定程度影响数据备份和复制的完整性。和redo持久化策略相同,可取值有0,1,N。默认为0,表示写入操作系统缓冲区,异步flush到磁盘。该值为1表示同步写入磁盘。为N则表示每写N次操作系统缓冲就执行一次刷新操作。

总结一下,本文是数据库事务系列文章的第三篇,核心介绍了MySQL的单机跨行事务模型,其中对隔离性所涉及到的锁技术、MVCC机制进行了比较详细的说明。对事务原子性、持久性等相关特性也进行简单的分析和说明。接着笔者将会带大家一起聊聊分布式事务模型,看看和单机事务模型到底有何区别。

推荐学习:MySQL教程

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

48

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

44

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

37

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

22

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

19

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

3

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

268

2026.02.27

deepseek在线提问
deepseek在线提问

本合集汇总了DeepSeek在线提问技巧与免登录使用入口,助你快速上手AI对话、写作、分析等功能。阅读专题下面的文章了解更多详细内容。

51

2026.02.27

AO3官网直接进入
AO3官网直接进入

AO3官网最新入口合集,汇总2026年可用官方及镜像链接,助你快速稳定访问Archive of Our Own平台。阅读专题下面的文章了解更多详细内容。

430

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 2.4万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 842人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号