MySQL: MySQL事务与ACID特性详解
最后更新:2026-08-26
事务保证了数据操作的原子性——要么全成功,要么全失败。
本课讲解事务的原理和使用。
graph TB
A[START TRANSACTION] --> B[执行SQL操作]
B --> C{是否成功?}
C -->|全部成功| D[COMMIT 提交]
C -->|部分失败| E[ROLLBACK 回滚]
B --> F[SAVEPOINT 保存点]
F --> G[ROLLBACK TO sp]
G --> B
D --> H[数据持久化]
E --> I[数据恢复到事务前]
1. 你将学到
- 事务的 ACID 特性
- START TRANSACTION / COMMIT / ROLLBACK
- SAVEPOINT 保存点
- 自动提交 AUTOCOMMIT
- 事务隔离级别
2. 转账的真实故事
(1) 痛点:转账中途失败
A 转 100 元给 B:
SQL
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 成功
-- 此时系统崩溃
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 未执行
结果:A 少了 100,B 没收到——钱凭空消失。
(2) 事务的解法
SQL
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 两条都成功才提交
3. ACID 特性
| 特性 | 说明 | 实现方式 |
|---|---|---|
| Atomicity 原子性 | 事务是不可分割的工作单元 | undo log |
| Consistency 一致性 | 事务前后数据保持一致 | 应用层+约束 |
| Isolation 隔离性 | 并发事务互不干扰 | 锁+MVCC |
| Durability 持久性 | 提交后数据永久保存 | redo log |
4. 事务操作
▶ 示例:基本事务
SQL
-- 开始事务
START TRANSACTION;
-- 执行操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 提交事务
COMMIT;
-- 或回滚事务
ROLLBACK;
▶ 示例:SAVEPOINT 保存点
SQL
START TRANSACTION;
INSERT INTO orders (customer_id, amount) VALUES (1, 100);
SAVEPOINT sp1;
INSERT INTO order_items (order_id, product_id) VALUES (LAST_INSERT_ID(), 1);
SAVEPOINT sp2;
-- 回滚到 sp1(保留第一条 INSERT)
ROLLBACK TO sp1;
-- 提交(只保留第一条 INSERT)
COMMIT;
5. 自动提交
▶ 示例:AUTOCOMMIT
SQL
-- 查看自动提交状态
SELECT @@autocommit;
-- 关闭自动提交
SET autocommit = 0;
-- 开启自动提交
SET autocommit = 1;
| autocommit | 说明 |
|---|---|
| 1(默认) | 每条语句自动提交 |
| 0 | 需要手动 COMMIT |
6. 事务隔离级别
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | 最高 |
| READ COMMITTED | ❌ | ✅ | ✅ | 高 |
| REPEATABLE READ(默认) | ❌ | ❌ | ✅ | 中 |
| SERIALIZABLE | ❌ | ❌ | ❌ | 最低 |
▶ 示例:设置隔离级别
SQL
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
7. 并发问题演示
(1) 脏读
SQL
-- 事务A
START TRANSACTION;
UPDATE accounts SET balance = 1000 WHERE id = 1;
-- 未提交
-- 事务B(READ UNCOMMITTED)
SELECT balance FROM accounts WHERE id = 1; -- 读到 1000(脏数据)
-- 事务A
ROLLBACK; -- 回滚
-- 事务B读到的数据是无效的
(2) 不可重复读
SQL
-- 事务A
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1; -- 读到 500
-- 事务B
UPDATE accounts SET balance = 1000 WHERE id = 1;
COMMIT;
-- 事务A
SELECT balance FROM accounts WHERE id = 1; -- 读到 1000(不一致)
COMMIT;
❓ 常见问题
Q MySQL 默认隔离级别?
A REPEATABLE READ(可重复读),使用 MVCC + 间隙锁实现。
Q 事务不提交会怎样?
A 锁一直持有,其他事务可能等待超时。
Q DDL 能回滚吗?
A 不能,DDL 自动提交无法回滚。
Q 隐式提交什么意思?
A DDL 语句(CREATE/ALTER/DROP)执行时会自动 COMMIT 当前事务,这就是隐式提交。
Q 大事务有什么风险?
A 锁持有时间长、undo log 膨胀、主从延迟增大。建议拆分为小事务。
📖 小节
- 事务保证操作的原子性,要么全成功要么全失败
- ACID:原子性、一致性、隔离性、持久性
- COMMIT 提交,ROLLBACK 回滚,SAVEPOINT 保存点
- 隔离级别控制并发问题:脏读、不可重复读、幻读
- MySQL 默认 REPEATABLE READ
📝 作业
-
基础题(难度⭐):编写一个转账事务,从账户 A 转 100 到账户 B。
-
进阶题(难度⭐⭐):使用 SAVEPOINT 实现部分回滚。
-
挑战题(难度⭐⭐⭐):演示不可重复读问题,并用 REPEATABLE READ 隔离级别解决。