目录
[1. 原子性(Atomicity)](#1. 原子性(Atomicity))
[2. 一致性(Consistency)](#2. 一致性(Consistency))
[3. 隔离性(Isolation)](#3. 隔离性(Isolation))
[4. 持久性(Durability)](#4. 持久性(Durability))
[1. 确认存储引擎支持](#1. 确认存储引擎支持)
[2. 基本语法](#2. 基本语法)
[3. 实战演示:回滚 vs 提交](#3. 实战演示:回滚 vs 提交)
[4. 高级技巧:保存点(Savepoint)](#4. 高级技巧:保存点(Savepoint))
[5. 自动提交 vs 手动提交](#5. 自动提交 vs 手动提交)
[1. 四种隔离级别概览](#1. 四种隔离级别概览)
[2. 查看和设置隔离级别](#2. 查看和设置隔离级别)
[3. 三大并发问题详解](#3. 三大并发问题详解)
[🔴 问题一:脏读(Dirty Read)](#🔴 问题一:脏读(Dirty Read))
[🟡 问题二:不可重复读(Non-repeatable Read)](#🟡 问题二:不可重复读(Non-repeatable Read))
[🟢 问题三:幻读(Phantom Read)](#🟢 问题三:幻读(Phantom Read))
[4. 串行化(SERIALIZABLE)](#4. 串行化(SERIALIZABLE))
[✅ 推荐做法](#✅ 推荐做法)
[❌ 避免陷阱](#❌ 避免陷阱)
一、什么是事务?
想象一下这样的场景:张三给李四转账100元。这个操作涉及两条SQL语句:
- 张三的账户余额减少100元
- 李四的账户余额增加100元
如果第一条执行成功,第二条却失败了,会发生什么?张三的钱扣了,李四却没收到,这显然不行!
事务(Transaction) 就是为了解决这个问题而生的。它将一组SQL语句打包成一个整体,要么全部成功,要么全部失败。在事务执行过程中,数据库会保证以下四点:
- 转账前后总金额不变
- 结果永久保存
- 执行过程不受其他事务干扰
- 不会出现"钱扣了但对方没收到"的中间状态
这正是事务的ACID特性在发挥作用。
二、事务的四大基石:ACID特性
1. 原子性(Atomicity)
"要么全做,要么全不做"
事务中的所有操作是一个不可分割的整体。如果执行过程中发生错误,整个事务会**回滚(Rollback)**到初始状态,就像从未执行过一样。
2. 一致性(Consistency)
"数据始终符合业务规则"
事务执行前后,数据库的完整性约束不被破坏。比如转账前后,两人的总金额必须保持2000元不变。
3. 隔离性(Isolation)
"各干各的,互不干扰"
多个并发事务同时操作时,彼此之间不会相互影响。数据库通过不同的隔离级别来平衡性能与安全性。
4. 持久性(Durability)
"一旦提交,永不丢失"
事务提交后,对数据的修改会永久写入存储介质,即使系统崩溃也不会丢失。
三、实战:如何在MySQL中使用事务?
1. 确认存储引擎支持
只有支持事务的存储引擎才能使用事务功能。在MySQL中,InnoDB是默认支持事务的引擎:
SHOW ENGINES;
2. 基本语法
sql
-- 开启事务
START TRANSACTION; -- 或 BEGIN;
-- 执行一系列SQL操作
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三';
UPDATE bank_account SET balance = balance + 100 WHERE name = '李四';
-- 提交事务(永久保存)
COMMIT;
-- 或者回滚事务(撤销所有操作)
ROLLBACK;
3. 实战演示:回滚 vs 提交
场景一:执行后回滚
sql
START TRANSACTION;
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三';
UPDATE bank_account SET balance = balance + 100 WHERE name = '李四';
-- 此时查询会发现余额已变
SELECT * FROM bank_account;
-- 但执行回滚后
ROLLBACK;
-- 再次查询,数据恢复原状!
场景二:执行后提交
sql
BEGIN;
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三';
UPDATE bank_account SET balance = balance + 100 WHERE name = '李四';
COMMIT;
-- 提交后,修改永久生效,无法回滚
4. 高级技巧:保存点(Savepoint)
在长事务中,可以设置保存点实现部分回滚:
sql
START TRANSACTION;
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三';
SAVEPOINT point1; -- 设置保存点
UPDATE bank_account SET balance = balance - 100 WHERE name = '张三';
SAVEPOINT point2;
INSERT INTO bank_account VALUES (NULL, '王五', 1000);
-- 回滚到point2,插入操作被撤销,但之前的更新保留
ROLLBACK TO point2;
-- 回滚到point1,第二次更新也被撤销
ROLLBACK TO point1;
-- 完全回滚
ROLLBACK;
5. 自动提交 vs 手动提交
MySQL默认开启自动提交(autocommit=ON),每条SQL都是一个独立事务。
sql
-- 查看当前设置
SHOW VARIABLES LIKE 'autocommit';
-- 关闭自动提交,进入手动模式
SET AUTOCOMMIT = 0; -- 或 SET AUTOCOMMIT = OFF;
-- 手动模式下,需要显式COMMIT或ROLLBACK
UPDATE bank_account SET balance = 500 WHERE id = 1;
COMMIT; -- 必须手动提交才生效
⚠️ 注意 :一旦使用
START TRANSACTION开启事务,无论autocommit设置如何,都必须手动COMMIT或ROLLBACK。
四、事务隔离级别
当多个用户同时操作数据库时,如何保证数据一致性?这就涉及到事务隔离级别。MySQL InnoDB引擎提供四种隔离级别,它们在性能和安全性之间做出不同取舍。
1. 四种隔离级别概览
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | 最高 |
| READ COMMITTED | ❌ | ✅ | ✅ | 较高 |
| REPEATABLE READ (默认) | ❌ | ❌ | 大部分❌ | 中等 |
| SERIALIZABLE | ❌ | ❌ | ❌ | 最低 |
2. 查看和设置隔离级别
sql
-- 查看当前会话隔离级别
SELECT @@SESSION.transaction_isolation;
-- 查看全局隔离级别
SELECT @@GLOBAL.transaction_isolation;
-- 设置会话级隔离级别(仅当前连接生效)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局隔离级别(新连接生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
3. 三大并发问题详解
🔴 问题一:脏读(Dirty Read)
发生场景 :**READ UNCOMMITTED**级别
现象 :事务A读取了事务B未提交的数据,如果事务B随后回滚,事务A读到的就是"脏数据"。
重现过程:
- 事务A插入一条记录但不提交
- 事务B读取到了这条未提交的记录
- 事务A回滚,记录消失
- 事务B读到的数据成了"幽灵"
💡 结论 :生产环境严禁使用
READ UNCOMMITTED!
🟡 问题二:不可重复读(Non-repeatable Read)
发生场景 :READ COMMITTED级别
现象:同一事务内,两次读取同一行数据,结果不一致(因为其他事务修改并提交了)。
重现过程:
- 事务A第一次查询"王五"余额为2000
- 事务B将"王五"余额改为1000并提交
- 事务A再次查询,发现余额变成1000
- 同一事务内,相同查询得到不同结果!
🟢 问题三:幻读(Phantom Read)
发生场景 :REPEATABLE READ级别(理论上存在,InnoDB通过Next-Key锁大幅缓解)
现象:同一事务内,两次查询同一范围的数据,行数不一致(因为其他事务插入了新记录)。
重现过程:
- 事务A查询所有余额>1000的记录,得到3条
- 事务B插入一条新记录并提交
- 事务A再次查询同一条件,得到4条
- 仿佛出现了"幻影"行!
✨ MySQL的优化 :InnoDB引擎在
REPEATABLE READ级别下使用Next-Key锁(记录锁+间隙锁),有效解决了大部分幻读问题。
4. 串行化(SERIALIZABLE)
最高隔离级别,所有事务串行执行,彻底解决并发问题,但性能代价极高,通常只用于对一致性要求极端严格的场景。

五、最佳实践与建议
✅ 推荐做法
- 默认使用
REPEATABLE READ:MySQL的默认隔离级别,在安全性和性能间取得良好平衡 - 短事务原则:事务包含的SQL越少、执行时间越短越好
- 明确提交/回滚:不要依赖自动提交,显式控制事务边界
- 异常处理:在代码中捕获异常并及时回滚
- 避免长事务:长事务会占用锁资源,影响并发性能
❌ 避免陷阱
- 不要在事务中进行网络请求、文件IO等耗时操作
- 避免在事务中等待用户输入
- 不要随意降低隔离级别换取性能
- 警惕隐式提交(如DDL语句会自动提交当前事务)