Mysql事务底层原理分析及说明

 更新时间:2026年06月23日 08:55:34   作者:龙门吹雪  
这段文章详细解释了事务的概念、特性、使用方法和隔离级别,并重点介绍了MVCC机制及其在InnoDB存储引擎中的应用,文章还描述了事务的执行流程和背后的原理,帮助理解数据库在高并发场景下的数据一致性和完整性保障机制

一、事务的概念

事务(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 ''''@''localhost'' to database

    mysql ERROR 1044 (42000): Access denied for user ''''@''loca

    这篇文章主要介绍了mysql下提示ERROR 1044 (42000): Access denied for user ''@'localhost' to database,需要的朋友可以参考下
    2015-09-09
  • mysql删除语句超详细汇总

    mysql删除语句超详细汇总

    这篇文章主要给大家介绍了关于mysql删除语句超详细汇总的相关资料,SQL是用于访问和处理数据库的标准的计算机语言,简称结构化查询语言,SQL中的删除语句有多种方法,这里总结下,需要的朋友可以参考下
    2023-08-08
  • MySQL 权限撤销(REVOKE)机制:从语法到安全实践指南

    MySQL 权限撤销(REVOKE)机制:从语法到安全实践指南

    在MySQL的用户权限管理体系中,REVOKE语句是用于撤销已授予用户的特定权限的核心命令,本文将围绕REVOKE的语法规范、权限作用域、常见误区及安全最佳实践展开深入解析,感兴趣的朋友跟随小编一起看看吧
    2026-06-06
  • 防止mysql重复插入记录的方法

    防止mysql重复插入记录的方法

    这篇文章主要为大家详细介绍了防止mysql重复插入记录的方法,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • mysql安装图解总结

    mysql安装图解总结

    本篇文章给大家总结了在各种电脑环境系统下安装MYSQL的图解过程,以及遇到问题后的解决办法。
    2018-06-06
  • MySQL中SQL模式的特点总结

    MySQL中SQL模式的特点总结

    这篇文章主要给大家总结介绍了关于MySQL中SQL模式特点的相关资料,文章介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • 在VB.NET应用中使用MySQL的方法

    在VB.NET应用中使用MySQL的方法

    这篇文章主要介绍了在VB.NET应用中使用MySQL的方法,操作基于Visual Studio IDE进行,需要的朋友可以参考下
    2015-06-06
  • MySQL数据库配置信息查看与修改方法详解

    MySQL数据库配置信息查看与修改方法详解

    我们通常把在项目中使用的常量收集在一个文件,这个文件就是配置文件,下面这篇文章主要给大家介绍了关于MySQL数据库配置信息查看与修改的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • 使用MySQL唯一索引的注意事项及说明

    使用MySQL唯一索引的注意事项及说明

    这篇文章主要介绍了使用MySQL唯一索引的注意事项及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • MySQL中的InnoDB单表访问过程

    MySQL中的InnoDB单表访问过程

    这篇文章主要介绍了MySQL中的InnoDB单表访问过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06

最新评论