事务四大特性深度剖析
候选人小钱在阿里 P6 面试中,面试官问:
"什么是事务?事务有哪些特性?"
小钱秒答:"ACID,原子性、一致性、隔离性、持久性。"
面试官点点头:"那 MySQL 是怎么保证原子性的?"
小钱说:"用 undo log 回滚。"面试官:"undo log 怎么实现?redo log 和 undo log 的区别是什么?"
小张愣了一下:"undo 是回滚用的,redo 是...重做?"
面试官:"一致性怎么保证?"
小钱答不上来了。
【面试官心理】
ACID 是 MySQL 面试的必考题。能把四个特性说出来的占 90%,能讲清每个特性的实现机制的占 30%,能把四个特性串联成一个完整故事的占 10%。这道题是区分"背过八股"和"真正理解"的分水岭。
一、ACID 是什么 🔴
1.1 四大特性定义
原子性(Atomicity):事务是最小执行单位,要么全成功,要么全失败
一致性(Consistency):事务执行前后,数据库状态必须保持一致
隔离性(Isolation):并发执行的事务互不干扰
持久性(Durability):事务提交后,数据永久保存
1.2 ❌ 错误理解
候选人原话:"一致性就是指事务执行完后,数据是一致的,比如转账前后总额不变。"
问题诊断:
- 把一致性当成了"结果",忽略了它是"约束"
- 一致性不是事务保证的,是由业务规则 + 其他三个特性共同保证的
- 原子性、隔离性、持久性是手段,一致性是目标
面试官内心 OS:这个候选人背了概念,但没有理解 ACID 之间的关系。
💡
理解一致性的关键:一致性是业务层面的约束(如余额不能为负),而原子性、隔离性、持久性是 MySQL 保证一致性的手段。如果业务本身定义了错误的一致性规则(如允许余额为负),MySQL 也会执行。
二、原子性:要么全做,要么全不做 🔴
2.1 undo log 的工作原理
MySQL 的原子性靠 undo log 实现。undo log 记录了数据修改前的值(反向操作),用于回滚。
graph TD
A[事务开始] --> B["UPDATE orders SET status = 1 WHERE id = 100"]
B --> C["写入 undo log: id=100, status=0"]
B --> D["修改数据页: status = 1"]
C --> E{事务提交还是回滚?}
E -->|提交| F[清空 undo log]
E -->|回滚| G["读取 undo log: id=100, status=0"]
G --> H["执行反向操作: status = 0"]
2.2 undo log 的结构
-- undo log 记录的内容
{
"table_name": "orders",
"primary_key": "id=100",
"old_value": {"status": 0, "amount": 100},
"new_value": {"status": 1, "amount": 200},
"undo_type": "UPDATE"
}
2.3 undo log 的类型
-- 查看 undo log 使用情况
SELECT * FROM information_schema.INNODB_TRX; -- 事务信息
SELECT * FROM information_schema.INNODB_UNDO_LOGS; -- undo log 状态
【面试官心理】
我问他原子性的实现,很多人会说"用事务控制",但说不清楚具体机制。能说出 undo log 并解释其工作原理的,基本都有源码阅读经验。
三、持久性:提交了就不会丢 🔴
3.1 redo log 的工作原理
MySQL 的持久性靠 redo log 实现。redo log 记录了数据修改后的值(正向操作),用于崩溃恢复。
为什么需要 redo log?
- 数据页太大,直接写磁盘太慢(随机 IO)
- redo log 是顺序写,性能高
- 事务提交时,写 redo log 即可认为持久化成功
3.2 redo log 的两阶段提交
graph LR
A[事务开始] --> B["UPDATE 操作"]
B --> C["修改数据页(内存)"]
C --> D["写入 redo log (prepare)"]
D --> E["写入 binlog"]
E --> F["提交事务 (commit)"]
F --> G["redo log 标记 commit"]
H[崩溃恢复] --> I{binlog 有记录?}
I -->|是| J{redo log 是 commit?}
I -->|否| K[回滚事务]
J -->|是| L[重做事务]
J -->|否| K
两阶段提交保证:
- binlog 和 redo log 的一致性
- 崩溃时能恢复到正确状态
⚠️
两阶段提交是 MySQL 最常被追问的知识点之一。如果 redo log 和 binlog 的状态不一致,会导致数据丢失或数据不一致。面试时被追问两阶段提交,要能画图说明。
3.3 redo log 和 undo log 的区别
四、隔离性:并发控制的核心 🔴
4.1 并发问题的来源
graph TD
A[并发事务] --> B["脏读: 读到其他事务未提交的数据"]
A --> C["不可重复读: 同一事务两次读结果不同"]
A --> D["幻读: 同一事务两次查询结果数不同"]
4.2 隔离级别与问题对照
-- 查看当前隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';
-- REPEATABLE-READ
-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
【面试官心理】
隔离级别这张表几乎是面试必考。能背下来的占 80%,能解释 MySQL 在 RR 级别下如何用 MVCC 解决幻读的占 30%。这是 P6 和 P7 的分水岭。
五、一致性:最终目标 🟡
5.1 一致性的三层含义
应用层面一致性:业务规则约束(如余额 >= 0)
数据库层面一致性:ACID 中的 C,由 AID 保证
分布式一致性:CAP 理论中的 C(P2C 架构中是最终一致)
5.2 事务一致性的保证
graph TD
A[一致性目标] --> B[原子性 保证操作全做或全不做]
A --> C[隔离性 保证并发执行互不干扰]
A --> D[持久性 保证提交后不丢失]
B --> E[数据库层面一致性]
C --> E
D --> E
【面试官心理】
我问他一致性的实现,很多人会卡壳。因为一致性不是一个独立实现的特性,而是其他三个特性的结果。能讲清楚这个关系的,基本都是 P7 以上的水准。
六、面试追问链
第一层:ACID 是什么?
第二层:MySQL 怎么实现原子性?
第三层:undo log 和 redo log 的区别?
- 候选人:undo 回滚未提交,redo 恢复已提交
第四层:两阶段提交是什么?为什么需要?
第五层:一致性怎么保证?
七、生产避坑
7.1 长事务的危害
-- ❌ 长事务:占用锁资源,导致其他事务等待
START TRANSACTION;
-- 业务处理...
-- 网络延迟...
-- 用户思考...
-- 30 分钟后才提交
COMMIT;
-- ✅ 正确做法:拆分事务
-- 将大批量操作拆分成小批次
FOR batch IN batches:
START TRANSACTION;
-- 处理 1000 条
COMMIT;
7.2 监控事务超时
-- 查看当前长事务
SELECT * FROM information_schema.INNODB_TRX
WHERE trx_started < NOW() - INTERVAL 60 SECOND;
💡
生产环境中,事务超时时间建议设置在 30 秒以内,避免长时间占用锁资源导致其他请求堆积。