跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
SQLPay算法

MySQL 事务的核心概念与 ACID 属性详解

MySQL 事务的核心概念及其解决的问题。在无控制的情况下,并发 CURD 操作可能导致数据不一致,如转账过程中的中间状态。为解决此问题,引入了事务机制,其核心在于满足 ACID 四大属性:原子性、一致性、隔离性和持久性。文章详细阐述了事务的定义、为何需要事务、版本支持(仅 InnoDB 支持)、提交方式(自动/手动)、常见操作及回滚机制,并通过多个终端演示验证了事务在不同异常情况下的行为,强调了正确理解事务对于保证数据完整性的关键作用。

不知所云发布于 2026/3/24更新于 2026/10/922K 浏览
MySQL 事务的核心概念与 ACID 属性详解

一、CURD 不加控制,会有什么问题

MySQL 存储数据,可能被多个客户端同时访问。mysqld 可能一次接受到多个关于 CURD 的请求,且内部采用多线程完成数据存储等工作,必然存在对数据的并发访问场景,会导致多请求并发可能产生的异常结果。

例如同行转账,按道理是'我减 100,你加 100',但因为使用同一张表,可能在我减 100 时因网络或数据库问题导致未给你加 100,整个操作出现中间过程(我减了但你没加)。在这种情况下允许异常产生,一旦操作未完成应将减掉的 100 加回来,就像什么都没做,等待下次合适时再转账。这相当于转账后不要有中间过程,一旦出现异常直接回滚,保证和初始状态一样,这就是回滚操作。在高并发场景下数据或多或少都会出现问题,要求 MySQL 必须有针对此类问题的解决方案。

二、CURD 满足什么属性,能解决上述问题?

  1. 买票的过程得是原子的吧(要么不抢,要么抢到,出现中间状态会回滚)
  2. 买票互相应该不能影响吧(彼此之间得是割裂的)
  3. 买完票应该要永久有效吧(购买成功必须持久化)
  4. 买前和买后都要是确定的状态吧(不允许有不确定的状态)

三、什么是事务?

事务就是一组 DML 语句组成,这些语句在逻辑上存在相关性。单独一条是没有意义的,比如转账就应该至少有两条 SQL 语句,即我减 100,你加 100,整体在一起才有转账逻辑。所以事务一定要站在 MySQL 的上层去看待 SQL 语句,具体完成一个由多条 SQL 语句构成的应用层功能,在业务上有具体含义的动作。这一组 DML 语句要么全部成功,要么全部失败,是一个整体。MySQL 提供一种机制,保证我们达到这样的效果。事务还规定不同的客户端看到的数据是不相同的。

事务就是要做的或所做的事情,主要用于处理操作量大,复杂度高的数据。假设一种场景:你毕业了,学校的教务系统后台 MySQL 中不再需要你的数据,要删除你的所有信息,那么要删除你的基本信息(姓名,电话,籍贯等)的同时,也删除和你有关的其他信息,比如:你的各科成绩,你在校表现,甚至你在论坛发过的文章等。这样,就需要多条 MySQL 语句构成,那么所有这些操作合起来,就构成了一个事务。

正如上面所说,一个 MySQL 数据库,可不止你一个事务在运行,同一时刻甚至有大量的请求被包装成事务向 MySQL 服务器发起事务处理请求。而每条事务至少一条 SQL,最多很多 SQL,这样如果大家都访问同样的表数据,在不加保护的情况,就绝对会出现问题。甚至,因为事务由多条 SQL 构成,也会存在执行到一半出错或者不想再执行的情况,那么已经执行的怎么办呢?

所以,一个完整的事务,绝对不是简单的 SQL 集合,还需要满足如下四个属性:

  1. 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
  2. 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  3. 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交、读提交、可重复读和串行化。
  4. 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

MySQL 从技术上只要保证了 1、3、4,就可以做到 2,所以 1、3、4 是因,2 是果(还需要上层用户配合)。

上面四个属性,可以简称为 ACID。

  • 原子性(Atomicity,或称不可分割性)
  • 一致性(Consistency)
  • 隔离性(Isolation,又称独立性)
  • 持久性(Durability)

MySQL 需要帮不同的客户端处理不同的事务请求,所以运行期间在自身内部必然存在大量的事务,它必须得将事务按照先描述后组织的形式管理起来,所以 MySQL 会把这些事务打包描述成对象,然后放入到事务执行列表里,并帮我们解决一系列执行事务时可能出现的问题。

四、为什么会有事务

事务被 MySQL 编写者设计出来(不是天然就有的,而是使用过程中发现应用层需要才被设计出来的),本质是为了当应用程序访问数据库的时候,事务能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题。(你只需要说你的需求,其他的我帮你处理)可以想一下当我们使用事务时,要么提交,要么回滚,我们不会去考虑网络异常了,服务器宕机了,同时更改一个数据怎么办对吧?因此事务本质上是为了应用层服务的,而不是伴随着数据库系统天生就有的。

备注:后面把 MySQL 中的一行信息,称为一行记录。

五、事务的版本支持

在 MySQL 中只有使用了 InnoDB 数据库引擎的数据库或表才支持事务,MyISAM 不支持。

查看数据库引擎:

mysql> show engines \G
*************************** 1. row ***************************
       Engine: InnoDB
      Support: DEFAULT
      Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
          XA: YES
Savepoints: YES
*************************** 5. row ***************************
       Engine: MyISAM
      Support: YES
      Comment: MyISAM storage engine
Transactions: NO
          XA: NO
Savepoints: NO

六、事务的提交方式

事务的提交方式常见的有两种:自动提交、手动提交。

查看事务提交方式:

show variables like 'autocommit'; 

用 SET 来改变 MySQL 的自动提交模式:

SET AUTOCOMMIT=0;  # 禁止自动提交
SET AUTOCOMMIT=1;  # 开启自动提交

七、事务常见操作方式

简单银行用户表

create table if not exists account(   
    id int primary key,   
    name varchar(50) not null default '',   
    balance decimal(10,2) not null default 0.0 
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

7.1 正常演示 - 证明事务的开始与(定向)回滚

mysql> show variables like 'autocommit';  -- 查看事务是否自动提交。我们故意设置成自动提交,看看该选项是否影响 begin
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> start transaction;                 -- 开始一个事务 begin 也可以,推荐 begin
Query OK, 0 rows affected (0.00 sec)

mysql> savepoint save1;                   -- 创建一个保存点 save1(根据需求设置保存点)
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (1, '张三', 100);  -- 插入一条记录
Query OK, 1 row affected (0.05 sec)

mysql> savepoint save2;                   -- 创建一个保存点 save2
Query OK, 0 rows affected (0.01 sec)

mysql> insert into account values (2, '李四', 10000); -- 在插入一条记录
Query OK, 1 row affected (0.00 sec)

mysql> select * from account;             -- 两条记录都在了
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> rollback to save2;                 -- 回滚到保存点 save2(定向回滚)
Query OK, 0 rows affected (0.03 sec)

mysql> select * from account;             -- 一条记录没有了
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> rollback;                          -- 直接 rollback,回滚在最开始,哪怕你一个回滚点都没设置也可以
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account;             -- 所有刚刚的记录没有了
Empty set (0.00 sec)

commit;                                   -- 就是把该事务给提交了,无法回滚

但是一般我们很少手动 rollback,事务大多数都是为了非正常情况。

7.2 非正常演示 1 - 证明未 commit,客户端崩溃,MySQL 自动会回滚(隔离级别设置为读未提交)

-- 终端 A
mysql> select * from account;             -- 当前表内无数据
Empty set (0.00 sec)

mysql> show variables like 'autocommit';  -- 依旧自动提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> begin;                             -- 开启事务
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (1, '张三', 100);  -- 插入记录
Query OK, 1 row affected (0.00 sec)

mysql> select * from account;             -- 数据已经存在,但没有 commit,此时同时查看终端 B
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> Aborted                            -- ctrl + \ 异常终止 MySQL

-- 终端 B
mysql> select * from account;             -- 终端 A 崩溃前
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> select * from account;             -- 数据自动回滚
Empty set (0.00 sec)

7.3 非正常演示 2 - 证明 commit 了,客户端崩溃,MySQL 数据不会在受影响,已经持久化

-- 终端 A
mysql> show variables like 'autocommit';  -- 依旧自动提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> select * from account;             -- 当前表内无数据
Empty set (0.00 sec)

mysql> begin;                             -- 开启事务
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (1, '张三', 100);  -- 插入记录
Query OK, 1 row affected (0.00 sec)

mysql> commit;                            -- 提交事务
Query OK, 0 rows affected (0.04 sec)

mysql> Aborted                             -- ctrl + \ 异常终止 MySQL

-- 终端 B
mysql> select * from account;             -- 数据存在了,所以 commit 的作用是将数据持久化到 MySQL 中
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

7.4 非正常演示 3 - 对比试验。证明 begin 操作会自动更改提交方式,不会受 MySQL 是否自动提交影响

手动 begin 就必须手动 commit,跟是否是自动提交毫无关系

-- 终端 A
mysql> select *from account;               -- 查看历史数据
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> show variables like 'autocommit';  -- 查看事务提交方式
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> set autocommit=0;                  -- 关闭自动提交
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';  -- 查看关闭之后结果
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> begin;                             -- 开启事务
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (2, '李四', 10000); -- 插入记录
Query OK, 1 row affected (0.00 sec)

mysql> select *from account;              -- 查看插入记录,同时查看终端 B
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> Aborted                             -- 再次异常终止

-- 终端 B
mysql> select * from account;             -- 终端 A 崩溃前
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select * from account;             -- 终端 A 崩溃后,自动回滚
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

7.5 非正常演示 4 - 证明单条 SQL 与事务的关系

-- 实验一
-- 终端 A
mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> set autocommit=0;                  -- 关闭自动提交
Query OK, 0 rows affected (0.00 sec)

mysql> insert into account values (2, '李四', 10000); -- 插入记录
Query OK, 1 row affected (0.00 sec)

mysql> select *from account;              -- 查看结果,已经插入。此时可以在查看终端 B
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> ^D                                -- ctrl + \ or ctrl + d,终止终端
Bye

-- 终端 B
mysql> select * from account;             -- 终端 A 崩溃前
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select * from account;             -- 终端 A 崩溃后
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

-- 实验二
-- 终端 A
mysql> show variables like 'autocommit';  -- 开启默认提交
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

mysql> select * from account;
+----+--------+--------+
| id | name   | blance |
+----+--------+--------+
|  1 | 张三   |  100.00 |
+----+--------+--------+
1 row in set (0.00 sec)

mysql> insert into account values (2, '李四', 10000);
Query OK, 1 row affected (0.01 sec)

mysql> select *from account;              -- 数据已经插入
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> Aborted                             -- 异常终止

-- 终端 B
mysql> select * from account;             -- 终端 A 崩溃前
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

mysql> select * from account;             -- 终端 A 崩溃后,并不影响,已经持久化。autocommit 起作用
+----+--------+----------+
| id | name   | blance |
+----+--------+----------+
|  1 | 张三   |  100.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

7.6 结论

  1. 只要输入 begin 或者 start transaction,事务便必须要通过 commit 提交,才会持久化,与是否设置 set autocommit 无关。
  2. 事务可以手动回滚,同时,当操作异常,MySQL 会自动回滚。
  3. 对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。(select 有特殊情况,因为 MySQL 有 MVCC)

从上面的例子,我们能看到事务本身的原子性 (回滚),持久性 (commit)。

7.7 事务操作注意事项

  1. 如果没有设置保存点,也可以回滚,只能回滚到事务的开始。直接使用 rollback(前提是事务还没有提交)。
  2. 如果一个事务被提交了(commit),则不可以回退(rollback)。
  3. 可以选择回退到哪个保存点。 savepoint save1; 设置保存点 rollback to save2; 回退保存点
  4. InnoDB 支持事务,MyISAM 不支持事务。
  5. 开始事务可以使 start transaction 或者 begin。

目录

  1. 一、CURD 不加控制,会有什么问题
  2. 二、CURD 满足什么属性,能解决上述问题?
  3. 三、什么是事务?
  4. 四、为什么会有事务
  5. 五、事务的版本支持
  6. 六、事务的提交方式
  7. 七、事务常见操作方式
  8. 7.1 正常演示 - 证明事务的开始与(定向)回滚
  9. 7.2 非正常演示 1 - 证明未 commit,客户端崩溃,MySQL 自动会回滚(隔离级别设置为读未提交)
  10. 7.3 非正常演示 2 - 证明 commit 了,客户端崩溃,MySQL 数据不会在受影响,已经持久化
  11. 7.4 非正常演示 3 - 对比试验。证明 begin 操作会自动更改提交方式,不会受 MySQL 是否自动提交影响
  12. 7.5 非正常演示 4 - 证明单条 SQL 与事务的关系
  13. 7.6 结论
  14. 7.7 事务操作注意事项

更多推荐文章

查看全部
  • openGauss 向量数据库能力与 RAG 应用场景解析
  • 基于 LangGraph 构建带记忆与人工干预的智能搜索机器人
  • 前端 JS 加载失败处理方案:重试与多源备份策略
  • Python 爬虫实战:爬取汽车之家车型与报价数据
  • 长亭雷池 WAF 部署与实战:Web 安全防护高频场景指南
  • AI 世界模型技术原理、研究进展与产业落地
  • 利用 Fiddler 代理抓包 JVM 发出的 HTTP 请求
  • C++高性能事件循环库libev封装实战
  • RoboChallenge 具身智能年度报告:4 万次真机评测揭示模型真实水平
  • 渗透测试基础概念、流程与内网渗透技术详解
  • 渐进式 AIGC 系统架构与功能特性:支持多模态大模型与 Agent 智能体
  • Python 代码打包为 EXE 完全指南
  • Quilter:把 PCB 设计交给物理驱动的 AI
  • 数据结构 (Java):单链表概念与基础实现
  • Jetson 部署 OpenClaw 接入飞书机器人实现本地 AI 助手
  • Python 量化实战:AKshare 获取股票基金期货全市场数据
  • WorkBuddy 安装与使用指南:腾讯 AI 原生桌面智能体工作台
  • Python 学习路线图:从基础到人工智能的十个关键阶段
  • QClaw 接入微信背后:AI 如何从聊天走向执行任务
  • GLM-4.5V-FP8 开源:多模态视觉推理新范式

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • SQL 美化和格式化

    在线格式化和美化您的 SQL 查询(它支持各种 SQL 方言)。 在线工具,SQL 美化和格式化在线工具,online

  • SQL转CSV/JSON/XML

    解析 INSERT 等受限 SQL,导出为 CSV、JSON、XML、YAML、HTML 表格(见页内语法说明)。 在线工具,SQL转CSV/JSON/XML在线工具,online

  • CSV 工具包

    CSV 与 JSON/XML/HTML/TSV/SQL 等互转,单页多 Tab。 在线工具,CSV 工具包在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online