InnoDB Undo Log机制全解析
这段文字是关于 InnoDB 存储引擎中“Undo Logs”(回滚日志) 的详细技术说明,主要解释了:
- Undo 日志是什么?
- 它们如何组织?
- 如何影响并发事务的数量?
下面我们逐层拆解、通俗易懂地讲解这段内容。
🧩 一、基本概念:什么是 Undo Log?
✅ Undo Log(回滚日志)
- 是每个读写事务产生的日志。
- 记录的是:“如何撤销这个事务对数据的最新修改”。
- 比如你把某行的
name='Alice'
改成'Bob'
,Undo Log 就记下“原来是 Alice”,这样如果要回滚,就可以恢复原值。
✅ 为什么需要 Undo Log?
- 事务回滚(Rollback):如果你执行
ROLLBACK
,数据库用 Undo Log 把改过的数据恢复回去。 - 一致性读(Consistent Read):当另一个事务在读数据时,不能看到未提交的修改。它会通过 Undo Log 找到“修改前”的版本来读 —— 这就是 MVCC(多版本并发控制) 的核心机制。
🏗️ 二、Undo Log 的存储结构(层级关系)
这些日志不是随便存的,而是有层级结构:
Undo Tablespace(撤销表空间)└── Rollback Segment(回滚段)└── Undo Log Segment(撤销日志段)└── Undo Log Records(撤销日志记录)
🔹 Undo Tablespace
- 存放所有 undo logs 的物理文件。
- 默认有一个系统 undo tablespace,也可以创建多个。
🔹 Rollback Segment(回滚段)
- 每个 undo tablespace 和 global temporary tablespace 都最多支持 128 个 rollback segments。
- 参数控制:
innodb_rollback_segments
控制实际使用的数量(默认通常是 128)。
🔹 Undo Log Segment / Undo Log
- 每个事务最多分配 4 个 undo logs,根据操作类型不同而不同。
📋 三、一个事务最多可以有 4 个 Undo Logs
每个事务根据它做的操作,会被分配最多 4 个 undo log:
操作类型 | Undo Log 用途 |
---|---|
对普通表的 INSERT | 单独一个 undo log |
对普通表的 UPDATE/DELETE | 单独一个 undo log |
对临时表的 INSERT | 单独一个 undo log |
对临时表的 UPDATE/DELETE | 单独一个 undo log |
⚠️ 注意:只有真正执行了这些操作,才会分配对应的 undo log。
✅ 举例说明:
事务行为 | 分配几个 undo log? |
---|---|
只插入普通表 (INSERT INTO t ) | 1 个 |
插入 + 更新普通表 | 2 个(INSERT 一个,UPDATE/DELETE 一个) |
只修改临时表 | 最多 2 个(INSERT 和 UPDATE/DELETE 各一个) |
普通表和临时表都改了 | 最多 4 个 |
📌 关键点:一旦分配,这个 undo log 就“绑定”给该事务,直到事务结束。
💡 四、Rollback Segment 中的 “Undo Slots”(槽位)
每个 rollback segment 能支持多少事务,取决于它有多少个“槽位”(Undo Slots)。
而这个数量和 InnoDB 页面大小(innodb_page_size) 有关:
InnoDB Page Size | Undo Slots per Rollback Segment |
---|---|
4KB | 256 |
8KB | 512 |
16KB | 1024 |
32KB | 2048 |
64KB | 4096 |
计算公式:
Undo Slots = innodb_page_size / 16
因为每个 undo log 占用一个 slot,所以:
- 一个 rollback segment 最多支持的 undo logs 数 = 上面这个值。
- 每个事务可能占用 1~4 个 undo logs → 所以能支持的并发事务数就减少了。
🧮 五、估算最大并发读写事务数
根据上面规则,我们可以估算 InnoDB 能支持多少个并发读写事务。
📌 公式总结:
✅ 情况 1:每个事务只做 INSERT 或 UPDATE/DELETE(普通表)
即:每个事务只使用 1 个 undo log(比如只 insert,或只 update)
最大并发事务数 = (innodb_page_size / 16) × innodb_rollback_segments × undo_tablespace 数量
👉 解释:
(innodb_page_size / 16)
:每个 rollback segment 的 slot 数innodb_rollback_segments
:每个表空间使用的 rollback segment 数量undo_tablespace 数量
:有几个 undo 表空间
✅ 情况 2:每个事务既做 INSERT 又做 UPDATE/DELETE(普通表)
即:每个事务使用 2 个 undo logs
最大并发事务数 = (innodb_page_size / 16 / 2) × innodb_rollback_segments × undo_tablespace 数量
👉 因为每个事务占两个 slot,所以除以 2。
✅ 情况 3:每个事务只对临时表做 INSERT
使用 global temporary tablespace 的 rollback segments(默认 128 个)
最大并发事务数 = (innodb_page_size / 16) × innodb_rollback_segments
📌 注意:这里不乘 undo tablespace 数量,因为临时表的 undo 日志只用 global temporary tablespace。
✅ 情况 4:每个事务对临时表做 INSERT + UPDATE/DELETE
占用两个 undo log(临时表专用)
最大并发事务数 = (innodb_page_size / 16 / 2) × innodb_rollback_segments
🚨 六、可能出现的问题
❗ 即使系统资源充足,也可能遇到“并发事务太多”的错误!
原因:
某个 rollback segment 的 undo slots 被用完了(比如某个 segment 被多个事务争抢),即使其他 segment 还有空位,也可能报错。
解决方法:
- 重试事务(MySQL 建议做法)
- 增加
innodb_rollback_segments
(但不能超过 128) - 增加 undo tablespace 数量(对普通表有效)
🌐 七、特殊优化:临时表的 Undo Log 不写 Redo Log!
- 修改用户定义的临时表时,其 undo log 存在 global temporary tablespace。
- 这些 undo log 不会写入 redo log(重做日志)。
- 为什么?
- 临时表的数据在服务器重启后就没了。
- 所以不需要 crash recovery(崩溃恢复)。
- 好处:避免了 redo log 的 I/O,提升性能!
✅ 总结:一句话理解
Undo Log 是事务用来回滚和实现 MVCC 的日志,InnoDB 通过“回滚段 + 槽位”管理它们。每个事务最多占 4 个槽位,因此最大并发事务数取决于页面大小、回滚段数量和 undo 表空间数量。
🧪 举个实际例子
假设:
innodb_page_size = 16KB
→ 每个 rollback segment 有 1024 个 slotinnodb_rollback_segments = 128
- 有 2 个 undo tablespace
问:最多支持多少个只做 INSERT 的事务?
答:
1024 × 128 × 2 = 262,144 个并发事务
问:如果每个事务都做 INSERT + UPDATE 呢?
答:
(1024 / 2) × 128 × 2 = 131,072 个并发事务
📚 附加:术语中英对照
中文 | 英文 |
---|---|
回滚日志 | Undo Log |
回滚段 | Rollback Segment |
撤销表空间 | Undo Tablespace |
全局临时表空间 | Global Temporary Tablespace |
一致性读 | Consistent Read |
多版本并发控制 | MVCC (Multi-Version Concurrency Control) |
槽位 | Slot |
重做日志 | Redo Log |
如果你正在调优高并发系统,理解这些机制可以帮助你合理配置 innodb_rollback_segments
、undo tablespace
数量,避免“并发事务过多”错误。