「一文搞懂」MySQL事务实现原理(mysql事务处理机制)
mhr18 2025-07-01 21:26 3 浏览 0 评论
本章内容
事务定义
事务指的是保证一组数据库操作,要么全部成功,要么全部失败。
在MySQL中,事务支持是在引擎层实现的,MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务。如:MySQL原生的MyISAM引擎就不支持事务,这也是MyISAM被InnoDB取代的重要原因之一。
事务ACID特性
事务的ACID特性指的是原子性、一致性、隔离性、持久性。
- 原子性(Atomicity):指的是事务中所有操作要么全部成功,要么全部失败。
- 一致性(Consistency):指的是事务执行前后,数据始终处于一致性状态,不会出现数据丢失。
- 隔离性(Isolation):指的是多个事务并发执行时,各事务在执行过程中所做的修改对其他事务不可见,直到该事务成功提交。
- 持久性(Durability):指的是事务一旦提交,对数据库所做的修改将会永久保存。
事务实现原理
原子性:通过undo log来实现。
持久性:通过bin log+redo log来实现。
隔离性:通过读写锁+MVCC来实现。
一致性:通过回滚、恢复以及在并发环境下的隔离做到一致性。
SQL执行流程
一条SQL语句的完整执行流程。如图所示:
其中:
- 步骤5:如果将参数innodb_flush_log_at_trx_commit设置成1,redo log在prepare阶段就需要调用fsync持久化一次。
- 步骤7:将在prepare阶段写入FS Page Cache的redo log添加Commit标识,后台每秒一次轮询刷盘时将Commit标识持久化到磁盘。
undo log(回滚日志)
undo log记录的是逻辑日志(即:SQL语句),它是InnoDB存储引擎用于实现多版本并发控制(MVCC)和事务回滚的一种日志记录机制。
undo log的主要作用:
- 回滚事务,恢复到修改前的数据。
- 实现 MVCC(多版本并发控制,Multi-Version Concurrency Control) 。
实现原理
当一个事务开始执行时,InnoDB会为该事务分配一个事务ID。
在事务执行过程中,所有的数据修改操作(如:插入、更新和删除)都会在内存中的缓冲池(Buffer Pool)进行。同时,对于每个数据修改操作,InnoDB会将修改前的数据版本记录到undo log中。注:Buffer Pool详见《「一文搞懂」MySQL缓冲池(buffer pool)》。
undo log存储在InnoDB的表空间(.ibdata格式文件)中,其中有一块区域名为rollback segment(回滚段),每个回滚段中有1024个undo-log segment,每个undo-log segment可存储一条数据,默认有128个回滚段,即:支持128*1024条记录同时存在。
如果事务执行失败,或者需要执行rollback操作,InnoDB可以利用undo log中的记录来还原数据的原始状态(即:执行数据回滚)。
当事务提交后,如果没有其他事务需要访问这些旧版本数据,InnoDB会在适当的时候回收并重用这些undo log空间。
版本链
undo log的实现方式是通过两个隐藏列trx_id(最近一次提交事务的ID)和roll_pointer(上一个版本的地址),建立一个版本链,并在事务中读取时生成一个ReadView(读视图),在Read Committed隔离级别下,每次读取都会生成一个读视图,而在Repeatable Read隔离级别下,只会在第一次读取时生成一个读视图。
如图所示:
对应的回滚段,如图所示:
图中,age字段的当前值为8,在查询这条记录时,不同时刻启动的事务会对应不同的read-view。在read-view A、read-view B、read-view C中,age字段的值分别为1、3、8,同一条记录在系统中会存在多个版本,即:数据库的多版本并发控制(MVCC)。对于read-view A,要得到age字段值1,就必须将当前值依次执行图中所有的回滚操作。同时,即使现在有另外一个事务正在将age字段的值由8改为9,这个事务与read-view A、read-view B、read-view C对应的事务不会冲突。
在MySQL5.5及以前的版本中,回滚日志与数据字典一起放在ibdata文件中,当系统判断没有事务会使用某个版本的回滚日志时,就会删除该版本的回滚日志。
redo log和bin log
详见:《「一文搞懂」MySQL如何保证数据不丢失》一文。
事务隔离级别
当数据库上有多个事务同时执行时,可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了隔离级别的概念。SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable ):
- 读未提交:是指一个事务还没提交时,该事务做的变更就能被其他事务看到。
- 读提交:是指一个事务提交之后,该事务做的变更才会被其他事务看到。
- 可重复读:是指一个事务执行过程中看到的数据,总是与该事务在启动时看到的数据一致。在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
- 串行化:是指对于同一行记录,写会加写锁,读会加读锁。当出现读写锁冲突时,后访问的事务必须等前一个事务执行完成,才能继续执行。
其中:
- 在可重复读隔离级别下,数据库在事务启动时创建一个视图,整个事务存在期间都使用这个视图。
- 在读提交隔离级别下,数据库在每个SQL语句开始执行时创建一个视图。
- 在读未提交隔离级别下,返回的是记录中的最新值,没有视图概念。
- 在串行化隔离级别下,使用加锁的方式来避免并行访问。
读提交和可重复读区别:
- 在可重复读隔离级别下:在事务开始时创建一致性视图,之后事务中的其他查询都共用这个一致性视图。
- 在读提交隔离级别下:每一个语句执行前都会重新创建一个新的视图。
MySQL默认隔离级别为:可重复读;Oracle默认隔离级别为:读提交。配置方式:启动参数 transaction-isolation,可以使用show variables来查看当前的值。
MVCC(多版本并发控制)
MySQL实现最高事务隔离级别串行化时,使用的是锁技术。在MySQL中使用的是读写锁,即:在读时加共享锁,写时加互斥锁。允许读读并行,读写以及写写都不能并行。
MVCC(Multi-Version Concurrency Control)多版本并发控制是一种并发控制机制,指维护一个数据的多个版本,使得读写操作没有冲突。具体实现就是采用快照读,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现需要依赖于数据库记录中的隐式字段、undo log版本链和Read View。
快照读和当前读
快照读:每次读取到的数据不一定是最新的数据,而是这条数据的快照版本,这样可以保证读写不互斥(读写分离),能够并发执行。
当前读:读取数据的最新版本,实现原理是对正在读的记录加锁,使得读写互斥,保证每次读取到的是数据库中最新的数据。
隐藏字段
当创建一张表时,InnoDB引擎会增加2个隐藏字段:
- DB_TRX_ID:修改表数据时,都会提交事务,每个事务都有一个唯一的ID,这个字段就记录了最近一次提交事务的ID。
- DB_ROLL_PTR:修改表数据时,旧版本的数据都会被记录到undo log日志中,每个版本的数据都有一个版本地址,这个字段记录的就是上一个版本的地址。
undo log版本链
详见:undo log章节中的版本链。
Read View(读视图)
Read View是一个保存事务ID的List列表,记录的是当前事务执行时,有哪些事务在执行,且还没有提交(即:当前系统还有哪些活跃的读写事务),用于判断当前事务是否可以读取undo log版本链中的哪个版本。
Read View基于一下几个字段实现:
- m_ids:当前系统中活跃的事务ID集合(即:未提交的事务集合)。
- min_trx_id:m_ids中最小的ID
- max_trx_id:下一个要分配的事务ID
- creator_trx_id:当前事务ID
如图所示:
事务在执行快照读时,可以通过如下规则来确定undo log版本链中的哪个版本数据可见:
1)DB_TRX_ID = creator_trx_id
如果这个版本(undo log版本链中的版本)的事务ID等于当前事务ID,表示数据记录的最后一次操作事务为当前事务,当前读视图可以读到这个版本的数据。
2)DB_TRX_ID < min_trx_id
如果这个版本的事务ID小于所有活跃事务ID,表示这个版本的数据不再被事务使用(即:事务已提交),当前读视图可以读到这个版本的数据。
3)DB_TRX_ID >= max_trx_id
如果这个版本的事务ID大于等于下一个要分配的事务ID,表示有新事务更新了这个版本的数据,这种情况下,当前读视图不可以读到这个版本的数据。
4)min_trx_id <= DB_TRX_ID < max_trx_id
如果这个版本的事务ID在当前系统中活跃的事务ID集合(m_ids)中,表示这个版本的数据被其他事务更新过,当前读视图不可以读到这个版本的数据。
如果这个版本的事务ID不在当前系统中活跃的事务ID集合(m_ids)中,表示是在其他事务提交后创建的读视图,当前读视图可以读到这个版本的数据。
MVCC在四种隔离级别下的区别
- Read Uncommitted(读未提交):事务总是读取到最新的数据,不需要MVCC。
- Serializable(串行化):事务总是顺序执行,写会加写锁,读会加读锁,不需要MVCC。
- READ COMMITTED(读已提交):在一个事务中每一次查询都会生成一个读视图,可以读到其他事务已提交的数据,会造成不可重复度的问题。
- REPEATABLE READ(可重复读):在一个事务中只有第一次查询时生成一个读视图,后面的查询都是使用该读视图,避免了不可重复读的问题。
常见问题
为什么建议尽量不要使用长事务?
由于长事务随时可能访问数据库中的任何数据,因此,在某个长事务提交之前,可能用到的回滚记录都必须保留在数据库中,从而占用大量存储空间。即:长事务会导致系统中存在很多很老的事务视图。
长事务除了对回滚段的影响,还占用锁资源,可能因锁资源无法及时释放而影响数据库的性能,甚至拖垮整个数据库。
【阅读推荐】
更多精彩内容(如:Redis、数据结构与算法、Nacos、G1垃圾回收器、CMS垃圾回收器、Kafka等)请移步【南秋同学】个人主页进行查阅。
【作者简介】
一枚热爱技术和生活的老贝比,专注于Java领域,关注【南秋同学】带你一起学习成长~
相关推荐
- Java面试宝典之问答系列(java面试回答)
-
以下内容,由兆隆IT云学院就业部根据多年成功就业服务经验提供:1.写出从数据库表Custom中查询No、Name、Num1、Num2并将Name以姓名显示、计算出的和以总和显示的SQL。SELECT...
- ADG (Active Data Guard) 数据容灾架构下,如何配置 Druid 连接池?
-
如上图的数据容灾架构下,上层应用如果使用Druid连接池,应该如何配置,才能在数据库集群节点切换甚至主备数据中心站点切换的情况下,上层应用不需要变动(无需修改配置也无需重启);即数据库节点宕机/...
- SpringBoot多数据源dynamic-datasource快速入门
-
一、简介dynamic-datasourc是一个基于SpringBoot的快速集成多数据源的启动器,其主要特性如下:支持数据源分组,适用于多种场景纯粹多库读写分离一主多从混合模式。支持...
- SpringBoot项目快速开发框架JeecgBoot——项目简介及系统架构!
-
项目简介及系统架构JeecgBoot是一款基于SpringBoot的开发平台,它采用前后端分离架构,集成的框架有SpringBoot2.x、SpringCloud、AntDesignof...
- 常见文件系统格式有哪些(文件系统类型有哪几种)
-
PART.01常见文件系统格式有哪些常见的文件系统格式有很多,通常根据使用场景(Windows、Linux、macOS、移动设备、U盘、硬盘等)有所不同。以下是一些主流和常见的文件系统格式及其特点:一...
- Oracle MySQL Operator部署集群(oracle mysql group by)
-
以下是使用OracleMySQLOperator部署MySQL集群的完整流程及关键注意事项:一、部署前准备安装MySQLOperator通过Helm安装Operator到Ku...
- LibreOffice加入"转向Linux"运动
-
LibreOffice项目正准备削减部分Windows支持,并鼓励用户切换到Linux系统。自Oracle放弃OpenOffice后,支持和指导LibreOffice开发的文档基金会对未来有着明确的观...
- Oracle Linux 10发布:UEK 8.1、后量子加密、增强开发工具等
-
IT之家6月28日消息,科技媒体linuxiac昨日(6月27日)发布博文,报道称OracleLinux10正式发布,完全二进制兼容(binarycompatibility...
- 【mykit-data】 数据库同步工具(数据库同步工具 开源)
-
项目介绍支持插件化、可视化的数据异构中间件,支持的数据异构方式如下MySQL<——>MySQL(增量、全量)MySQL<——>Oracle(增量、全量)Oracle...
- oracle关于xml的解析(oracle读取xml节点的属性值)
-
有时需要在存储过程中处理xml,oracle提供了相应的函数来进行处理,xmltype以及相关的函数。废话少说,上代码:selectxmltype(SIConfirmOutput).extract...
- 如何利用DBSync实现数据库同步(通过dblink同步数据库)
-
DBSync是一款通用型的数据库同步软件,能侦测数据表之间的差异,能实时同步差异数据,从而使双方始终保持一致。支持各种数据库,支持异构同步、增量同步,且提供永久免费版。本文介绍其功能特点及大致用法,供...
- MYSQL存储引擎InnoDB(八十):InnoDB静态数据加密
-
InnoDB支持独立表空间、通用表空间、mysql系统表空间、重做日志和撤消日志的静态数据加密。从MySQL8.0.16开始,还支持为模式和通用表空间设置加密默认值,这允许DBA控制在这些模...
- JDK高版本特性总结与ZGC实践(jdk高版本兼容低版本吗)
-
美团信息安全技术团队核心服务升级JDK17后,性能与稳定性大幅提升,机器成本降低了10%。高版本JDK与ZGC技术令人惊艳,且JavaAISDK最低支持JDK17。本文总结了JDK17的主要...
- 4 种 MySQL 同步 ES 方案,yyds!(两个mysql数据库自动同步的方法)
-
本文会先讲述数据同步的4种方案,并给出常用数据迁移工具,干货满满!不BB,上文章目录:1.前言在实际项目开发中,我们经常将MySQL作为业务数据库,ES作为查询数据库,用来实现读写分离,...
- 计算机Java培训课程包含哪些内容?其实就这六大块
-
不知不觉秋天已至,如果你还处于就业迷茫期,不如来学习Java。对于非科班小白来说,Java培训会更适合你。提前了解下计算机Java培训课程内容,会有助于你后续学习。下面,我就从六个部分为大家详细介绍...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Java面试宝典之问答系列(java面试回答)
- ADG (Active Data Guard) 数据容灾架构下,如何配置 Druid 连接池?
- SpringBoot多数据源dynamic-datasource快速入门
- SpringBoot项目快速开发框架JeecgBoot——项目简介及系统架构!
- 常见文件系统格式有哪些(文件系统类型有哪几种)
- Oracle MySQL Operator部署集群(oracle mysql group by)
- LibreOffice加入"转向Linux"运动
- Oracle Linux 10发布:UEK 8.1、后量子加密、增强开发工具等
- 【mykit-data】 数据库同步工具(数据库同步工具 开源)
- oracle关于xml的解析(oracle读取xml节点的属性值)
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)