原子性 Atomicity 如何保证
原子性的核心保证
原子性保证事务内的操作要么全部执行,要么全部不执行。MySQL InnoDB 通过 undo log 实现原子性。
START TRANSACTION;
INSERT INTO orders(id, user_id, amount) VALUES(1, 100, 200);
UPDATE inventory SET stock = stock - 1 WHERE id = 10;
DELETE FROM cart WHERE user_id = 100;
COMMIT; -- 全部生效
-- 或
ROLLBACK; -- 全部撤销
undo log 的工作原理
什么是 undo log
undo log(回滚日志)记录了事务执行过程中数据的旧版本。
执行过程:
① 开启事务
② 修改数据前,把旧值写入 undo log
③ 修改数据
④ 如果 ROLLBACK → 用 undo log 恢复旧值
⑤ 如果 COMMIT → undo log 可以清除(但 MVCC 需要保留一部分)
写入时机
-- 执行 UPDATE 时
UPDATE user SET name = '李四' WHERE id = 1;
-- InnoDB 内部执行:
-- 1. 记录旧值到 undo log: (id=1, name='张三')
-- 2. 将数据页中 name 改为 '李四'
-- 3. 标记该 undo log 归属当前事务
回滚过程
ROLLBACK 时:
1. 找到当前事务的所有 undo log
2. 从最新到最旧,逐一恢复数据
3. 每个 undo log 包含:
- 旧的行数据
- 修改的字段和旧值
- 事务 ID
undo log 的两种类型
INSERT 的 undo log
INSERT INTO user VALUES (100, '张三');
undo log 记录:
type: INSERT
table: user
hidden: 行被隐藏(标记删除)
回滚时:执行反向操作——删除该行。
UPDATE/DELETE 的 undo log
UPDATE user SET name = '李四' WHERE id = 100;
-- 原值 name = '张三'
undo log 记录:
type: UPDATE
table: user
old_values: {name: '张三', age: 25, ...}
回滚时:用旧值覆盖回。
undo log 的存储
Buffer Pool 磁盘
┌──────────────────┐ ┌─────────────────┐
│ undo log 页 │──flush→│ undo 表空间文件 │
│ (内存中) │ │ (ibdata1 / undo_001) │
└──────────────────┘ └─────────────────┘
- MySQL 5.7+:undo log 可以放在独立的 undo 表空间
- 默认存储在系统表空间(ibdata1)
- 建议:独立表空间,方便管理和回收
undo log 与 MVCC 的关系
undo log 不仅用于回滚,还用于多版本并发控制(MVCC):
-- 事务 A(未提交)
UPDATE user SET name = '李四' WHERE id = 1;
-- 事务 B(在事务 A 之前开始)
SELECT name FROM user WHERE id = 1;
-- 事务 B 看到的是 '张三'(undo log 中的旧版本)
MVCC 利用 undo log 构建"读视图":
当前行数据:'李四'(已修改,未提交)
undo log 链:'张三' → '王五' → ...
事务 B 根据自己的 Read View:
→ 只能看到 <= 自己快照版本的数据
→ 沿着 undo 链找到合适的版本
→ 返回答:'张三'
undo log 的生命周期
事务开始
│
├─ 写入 undo log
│
├─ 事务提交
│ │
│ ├─ 如果 MVCC 也需要这批 undo log
│ │ → 保留在 undo 链中(直到没有事务需要它)
│ │
│ └─ 如果所有可能读到它的事务都已结束
│ → 标记为可清除(purge)
│
└─ 事务回滚
→ 立即用 undo log 恢复数据
→ 完成后标记清除
purge 线程
-- 查看 purge 相关参数
SHOW STATUS LIKE 'Innodb_undo_truncations';
SHOW VARIABLES LIKE 'innodb_purge_threads';
SHOW VARIABLES LIKE 'innodb_purge_batch_size';
-- purge 线程自动清理:
-- 1. 已提交事务的 undo log
-- 2. 不再被 MVCC 引用的旧版本
-- 3. 标记删除的行(真正物理删除)
undo log 过多带来的问题
-- 大事务会产生大量 undo log
START TRANSACTION;
DELETE FROM orders WHERE id > 1000000; -- 100 万行
-- 产生 100 万条 undo log
-- undo 表空间暴涨
COMMIT;
问题:
1. undo 表空间膨胀
2. MVCC 旧版本链过长 → 查询变慢
3. purge 线程压力大
建议:
- 避免大事务(分批 DELETE/UPDATE)
- 监控 undo 表空间大小
- 配置合适的 undo 表空间大小
面试要点
- undo log 是原子性的基石:记录修改前的数据旧版本
- 两种类型:INSERT undo(反向删除)和 UPDATE/DELETE undo(旧值覆盖)
- 双重用途:事务回滚 + MVCC 多版本读
- 与 redo log 的区别:undo 记录旧值(回滚),redo 记录新值(故障恢复)
- 生命周期:事务提交后不一定立即清除,MVCC 还需要时保留
- LONGTEXT 事务:大事务的 undo log 可能巨大,注意监控
一句话总结:InnoDB 用 undo log 实现原子性——每次修改前先给数据"拍照存档",回滚时按图索骥恢复原状,同时它也是 MVCC 版本链的基石。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容