Mysql事务底层原理分析及说明
一、事务的概念
事务(Transaction)就是由一组 SQL 语句构成的逻辑工作单元。它就像是一个不可分割的整体,要么里面的操作全部成功,要么全部失败,用于在高并发场景下保证数据的一致性和完整性。

二、事务的特性
- 原子性(Atomicity):当前事务的执行要么全部成功,要么全部失败。原子性由undo log日志来保证。
- 一致性(Consistency):使用事务的最终目的,由原子性、隔离性、持久性及业务代码正确逻辑保证。
- 隔离性(Isolation):在事务并发执行时,它们内部的操作互相不干扰,由MVCC来保证。
- 持久性(Durability):一旦提交了事务,它对数据库的改变就是永久性的。持久性由redo log日志来保证。
三、事务的使用方法

在 MySQL 中,通常使用以下 SQL 语句来控制事务:
3.1 开启事务
-- 开启事务 start transaction; -- 或者使用 begin;
3.2 执行一系列 SQL 操作
-- 执行一系列 sql 语句 select * from t where id > 1; insert into t(a, b, c, d) values (3, 5, 5, 'beijing');
3.3 提交事务
-- 提交事务 commit;
3.4 回滚事务
-- 回滚事务 rollback;
注意:
MySQL 默认是 自动提交 (autocommit = 1) 模式。
这意味着你每执行一条 UPDATE 或 INSERT,它都会立即作为一个独立的事务提交。
如果要手动控制事务,必须先执行 START TRANSACTION 或将 autocommit 设为 0。

四、事务的隔离级别
MySQL InnoDB 存储引擎支持四种事务隔离级别:
1、读未提交(READ UNCOMMITTED):一个事务还没提交时,它做的变更就能够被其他事务看到。可能会产生脏读、不可重复读和幻读问题。
2、读已提交(READ COMMITTED):一个事务提交后,它做的变更才能被其他事务看到。
3、可重复读(REPEATABLE READ):MySQL的默认隔离级别。一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。
4、串行化(SERIALIZABLE):事务被强制串行执行,每个事务在执行时都会对其他事务影响的数据行进行加锁,从而避免并行访问。可以避免脏读、不可重复读和幻读问题,但并发性能最低。
查看和设置数据库的事务隔离级
-- 查看当前会话的隔离级别 select @@tx_isolation; -- 查看全局的隔离级别 select @@global.tx_isolation; -- 设置当前会话的事务隔离级别(设置全局隔离级别:将 SESSION 换为 GLOBAL) SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE -- mysql8 查看当前会话的事务隔离级别 SELECT @@SESSION.TRANSACTION_ISOLATION; -- mysql8 查看全局的事务隔离级别 select @@global.TRANSACTION_ISOLATION;
脏读:
- 一个事务(Transaction A)读取了另一个事务(Transaction B)已修改但未提交的数据。
- 如果事务 B 随后因为某种原因回滚(Rollback)了,那么事务 A 读到的数据就是无效的(即“脏”数据)
不可重复读:
- 针对同一行数据,指在同一个事务中,前后两次读取这行数据,读取的值不一样。
- 比如在事务 A 中,我第一次读取 ID=1 的记录,值是 100;事务 B 把这行改成了 200 并提交;我第二次读取 ID=1 的记录,发现值变成了 200。数据被改了。
幻读:
- 针对结果集的数量,指在同一个事务中,前后两次读取一个范围内的数据,获取的结果集的行数不一样。
- 比如如在事务 A 中,我第一次查“年龄 > 20”的所有记录,查出来 5 条;事务 B 插入了一条“年龄 = 25”的新记录并提交;我第二次再查“年龄 > 20”,发现出来了 6 条。多了一行。
4.1 读未提交
在这个级别下,一个事务可以读取到其他事务尚未提交(Commit)的修改。可能会产生脏读、不可重复读和幻读问题。

4.2 读已提交
一个事务提交后,其他事务才能看到变更的数据。读已提交隔离级别解决了脏读,但存在不可重复读、幻读现象。

4.3 可重复读
一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。该隔离级别可以解决脏读、不可重复读问题,但在特定场景下依然会存在幻读问题。

出现幻读的场景:

原因分析:
可重复读隔离级别下,会基于事务启动后第一次查询时建立的数据“快照”(Read View)来读取数据,它看不到其他事务在它之后插入或修改的任何数据。因此,在纯快照读的场景下,不会发生幻读。
但事务中存在如下 SQL 语句时,必须读取数据库中最新的、已提交的数据。为了保证数据一致性,它会忽略之前的快照,直接去读取当前数据库的最新状态。
- SELECT ... FOR UPDATE
- SELECT ... LOCK IN SHARE MODE
- UPDATE, DELETE, INSERT
上述例子中,T4 时刻,左边事务更新 id > 3 的记录,会忽略快照,而去数据库中查找最新的记录,从而查询到右边事务新增的记录,即更新了两条记录。
根据MVCC机制,修改的这两条记录的事务ID都会标记为左边事务的id,从而能在第三次查询中显示出来,即出现幻读。
解决方案:
在左边事务查询数据时增加读锁或写锁,阻止其他事务新增数据。

4.4 串行化
每个事务在执行时都会对其他事务影响的数据行进行加锁,从而避免并行访问。可以避免脏读、不可重复读和幻读问题,但并发性能最低。

五、事务的原理
5.1 核心概念
MVCC(Multi-Version Concurrency Control)多版本并发控制,通过保存数据的历史版本来实现并发控制,避免读写冲突,主要通过undo日志链来实现。
5.1.1 InnoDB 存储引擎的行格式

- DB_TRX_ID:最近修改事务ID
- DB_ROLL_PTR:回滚指针,指向 undo log 记录
每次事务对行数据进行修改时,都会新生成一行记录,并且 DB_TRX_ID 保存该事务的ID,DB_ROLL_PTR 保存上一次修改该行记录的 undo log 位置指针

5.1.2 ReadView 快照
事务读取时创建的视图,包含:
- creator_trx_id:创建者事务ID
- m_ids:活跃事务ID列表,不包含当前事务的ID
- min_trx_id:最小活跃事务ID
- max_trx_id:下一个要分配的事务ID
快照的过滤规则:
MVCC 在读取每一行数据时,会检查该行的版本号(trx_id)是否对当前事务可见。判断逻辑如下:
- 如果这行数据的
trx_id等于当前事务的 ID(即是你自己修改的) -> 可见 ✅ - 如果这行数据的
trx_id在快照的“活跃事务列表”中(即其他未提交事务修改的) -> 不可见 ❌ - 如果这行数据的
trx_id小于快照中的最小版本号(即其他事务在快照创建前修改并提交的) -> 可见 ✅ - 如果这行数据的
trx_id大于快照中的最大版本号(即其他事务在快照创建后修改的) -> 不可见 ❌

5.2 事务执行流程
事务执行流程如下:
①查询处理:客户端发送 SQL 请求到 MySQL Server 层
②数据加载:如果所需数据页不在 Buffer Pool 中,从磁盘加载到内存
③事务开始:执行 START TRANSACTION,分配事务 ID 并创建 Read View
④数据修改:在 Buffer Pool 中创建新版本数据,原数据写入 Undo Log
⑤日志记录:
- Undo Log:记录逻辑变化,支持 MVCC 和回滚
- Redo Log:记录物理变化,保证持久性
- Binlog:记录逻辑 SQL,用于主从复制和数据恢复
⑥两阶段提交:
- Prepare 阶段:Redo Log 写入磁盘并标记为 prepare 状态
- Commit 阶段:Binlog 写入磁盘,然后 Redo Log 标记为 commit 状态
⑦后台刷新:脏页由后台线程异步刷入磁盘

事务执行的内存结构与磁盘文件示意图:

5.3 隔离级别与MVCC关系
MVCC 主要用于实现 READ COMMITTED(读已提交)和 REPEATABLE READ(可重复读)这两种隔离级别。

总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
mysql ERROR 1044 (42000): Access denied for user ''''@''loca
这篇文章主要介绍了mysql下提示ERROR 1044 (42000): Access denied for user ''@'localhost' to database,需要的朋友可以参考下2015-09-09
MySQL 权限撤销(REVOKE)机制:从语法到安全实践指南
在MySQL的用户权限管理体系中,REVOKE语句是用于撤销已授予用户的特定权限的核心命令,本文将围绕REVOKE的语法规范、权限作用域、常见误区及安全最佳实践展开深入解析,感兴趣的朋友跟随小编一起看看吧2026-06-06


最新评论