原子性 Atomicity 如何保证

原子性 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
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容