事务四大特性
面试官问:"MySQL 事务的 ACID 是什么?每个特性分别是怎么实现的?"
小王说:"A 是原子性,I 是隔离性,C 是一致性,D 是持久性。原子性就是一起成功或一起失败。"
面试官追问:"那 InnoDB 是怎么保证原子性的?"
小王说:"...用 redo log?"
面试官继续追问:"redo log 不是保证持久性的吗?"
小王开始混乱了。
ACID 是 MySQL 事务的灵魂。但这四个字背后对应的具体实现机制,很多人搞不清楚。原子性、隔离性、持久性各自有各自的实现方式,redo log 和 undo log 各司其职。这道题能答清楚的候选人,对 MySQL 的理解已经到了一定深度。
一、ACID 四大特性 🔴
1.1 ACID 定义
1.2 深入理解一致性
一致性是最容易误解的概念。很多人以为一致性是数据库保证的,但实际上:
一致性 = 业务层面的约束 + 数据库的保证。
-- 转账场景:A 账户 1000 元,B 账户 1000 元
-- A 向 B 转账 500 元
-- 原子性:A 扣 500 和 B 加 500 要么都成功,要么都失败
-- 隔离性:并发转账时不会导致余额计算错误
-- 持久性:提交后即使数据库崩溃,余额也不能变
-- 以上三个特性保证了:
-- 1000 + 1000 = 1500 + 500 = 2000(一致性)
数据库本身无法理解"转账前后总金额不变"这种业务规则,但数据库通过 ACID 机制保证了技术层面的一致性。业务层面的一致性需要应用程序来保证。
1.3 ❌ 错误示范
候选人原话:"ACID 就是事务的四个特性,InnoDB 支持,MyISAM 不支持。"
问题诊断:
- 只知道概念,不理解每个特性的实现机制
- 把 ACID 当成一个整体,没有区分各自的技术实现
- 混淆了 redo log 和 undo log 的作用
候选人原话 2:"redo log 保证原子性,undo log 保证持久性。"
问题诊断:完全搞反了 redo log 和 undo log 的职责。
【面试官心理】
这道题我能从多个角度追问。基础问法是"ACID 是什么",进阶问法是"每个特性怎么实现",深入问法是"redo log 和 undo log 分别在什么时候写、什么时候刷盘"。能答出两阶段提交的,是真正理解 InnoDB 事务机制的候选人。
二、原子性:undo log 🔴
2.1 undo log 的作用
undo log 记录了数据修改前的值,用于事务回滚和 MVCC。
-- 事务开始
BEGIN;
-- 执行更新
UPDATE users SET balance = balance - 500 WHERE id = 1; -- A 账户扣 500
-- undo log 记录:
-- 用户 ID=1,balance 从 1000 改成了 500,undo log 记录旧值 1000
UPDATE users SET balance = balance + 500 WHERE id = 2; -- B 账户加 500
-- 此时如果回滚
ROLLBACK;
-- InnoDB 读取 undo log:
-- 用户 ID=1,balance 恢复到 1000
-- 用户 ID=2,balance 恢复到 1000
2.2 undo log 的结构
InnoDB 的 undo log 存储在系统表空间或独立的 undo 表空间中。每个事务有自己独立的 undo 页面链表。
undo log 链:
┌──────────────────────────────────────┐
│ UPDATE users SET balance=500 │ ← 最新操作
│ WHERE id=1; │
│ old_value: balance=1000 │
└──────────────────────────────────────┘
↓
┌──────────────────────────────────────┐
│ INSERT INTO orders (...) │ ← 更早的操作
│ row_id: 10086 │
└──────────────────────────────────────┘
2.3 undo log 的类型
三、持久性:redo log 🔴
3.1 为什么需要 redo log?
问题:事务提交后,数据需要持久化到磁盘。但如果每条 UPDATE 都直接刷盘,性能极差(随机 IO)。
解决:引入 redo log。先把操作记录到 redo log(顺序写),然后异步刷数据到磁盘。
刷盘策略:
1. 用户执行 UPDATE
2. 数据写入 Buffer Pool(内存)
3. 操作记录写入 redo log buffer(内存)
4. 事务提交时,调用 fsync 将 redo log 刷到磁盘
5. 后台线程异步将 Buffer Pool 中的脏页刷到磁盘
3.2 redo log 的刷盘策略
-- 查看 innodb_flush_log_at_trx_commit 配置
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
-- 配置值:
-- 1(默认):事务提交时刷 redo log 到磁盘,最安全
-- 0:每秒刷一次,可能丢失最多 1 秒的数据
-- 2:刷到操作系统缓存,不保证落盘,最快但不安全
⚠️
innodb_flush_log_at_trx_commit = 1 是 MySQL 默认也是最安全的刷盘策略。设置为 0 或 2 会提升写入性能,但牺牲持久性。在生产环境中,如果对数据可靠性有要求,不要改这个配置。
3.3 redo log 的结构
InnoDB 的 redo log 是循环写入的:
┌─────────────────────────────────────────────────────────────┐
│ redo log buffer │
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │
│ │ log1 │ log2 │ log3 │ log4 │ log5 │ │
│ └────┬────┴────┬────┴────┬────┴────┬────┴────┬────┘ │
└────────┼─────────┼─────────┼─────────┼─────────┼───────────┘
↓ ↓ ↓ ↓ ↓
┌─────────────────────────────────────────────────────────────┐
│ redo log file │
│ ┌─────────┬─────────┬─────────┬─────────┬─────────┐ │
│ │ log1 │ log2 │ log3 │ log4 │ log5 │ (循环) │
│ └─────────┴─────────┴─────────┴─────────┴─────────┘ │
└─────────────────────────────────────────────────────────────┘
redo log 文件是固定大小的(默认 48MB x 2)。写满后循环覆盖,所以 redo log 只能保存最近一段时间的操作。这也是 redo log 不适合做主从复制的原因(binlog 才是做主从复制的日志)。
四、隔离性:锁 + MVCC 🟡
4.1 隔离性的实现机制
InnoDB 通过锁机制和MVCC(多版本并发控制) 两套机制来实现隔离性。
- 锁机制:悲观并发控制,事务对数据加锁,阻塞其他事务
- MVCC:乐观并发控制,每个事务看到的是数据的历史快照,不阻塞读取
两者的关系:MVCC 解决读-读并发(完全不需要锁),锁机制解决读-写/写-写并发。
4.2 事务开启方式
-- 快照读:读取历史版本,不加锁
SELECT ...;
-- 当前读:读取最新版本,加锁
SELECT ... LOCK IN SHARE MODE;
SELECT ... FOR UPDATE;
INSERT ...;
UPDATE ...;
DELETE ...;
4.3 MVCC 的核心组件
- 隐藏列:每行数据有
trx_id(最近修改的事务ID)和 roll_pointer(指向 undo log 的指针)
- undo log 链:通过 roll_pointer 连接历史版本
- ReadView:事务开启时生成,决定能看到哪些版本
五、一致性的保障体系 🟡
5.1 数据库层面的一致性保证
- 原子性:undo log 保证回滚
- 隔离性:锁 + MVCC 保证并发正确性
- 持久性:redo log + 刷盘保证已提交数据不丢失
- 约束检查:唯一键、外键、CHECK 约束等数据库内置约束
5.2 两阶段提交保证
BEGIN;
UPDATE users SET balance = balance - 500 WHERE id = 1;
UPDATE users SET balance = balance + 500 WHERE id = 2;
COMMIT;
两阶段提交流程:
1. 写入 redo log(prepare 状态)
2. 写入 binlog
3. 提交事务(commit 状态)
崩溃恢复时:
- redo log prepare + binlog 有 → 提交
- redo log prepare + binlog 无 → 回滚
- redo log commit → 提交
【面试官心理】
问 ACID 的候选人里,能准确说出 redo log 和 undo log 分工的占 60%,能说清刷盘策略的占 30%,能说清两阶段提交细节的占 10%。这道题从 P5 问到 P7 都有很好的区分度。