当 MySQL InnoDB 引擎的 Redo Log 拯救了你的启动
Source: Dev.to
灯塔守护者的类比
把 InnoDB 的 redo log 想象成灯塔守护者的日志。
在现代 GPS 之前,灯塔守护者会仔细记录日志:
- “3:15 PM — 船只向北通过。”
- “4:22 PM — 为雾天点亮灯塔。”
- “5:03 PM — 风向转向西。”
如果出现任何问题,调查人员可以通过阅读日志来重建事发经过。
InnoDB 的工作方式相同。在进行任何更改之前,它会先写入 redo log:
“我即将更新表 users 中的第 47 行。以下是我要更改的内容。”
这项 预写日志(WAL) 是你在崩溃、断电以及宇宙射线翻转 RAM 位时的安全网。
**注意:**灯塔守护者可能会成为瓶颈。
几乎让我们崩溃的事务模式
我们的应用处理金融交易——速度极快。峰值时我们每秒处理约 5,000 次写入。
一个典型的事务如下:
-- Running thousands of times per second
START TRANSACTION;
UPDATE accounts
SET balance = balance - 100
WHERE user_id = ?;
INSERT INTO transaction_log
(user_id, amount, timestamp)
VALUES (?, 100, NOW());
UPDATE user_metadata
SET last_transaction = NOW()
WHERE user_id = ?;
COMMIT;
看起来很 innocuous(无害),对吧?错。 这种模式在悄悄地扼杀我们的 redo log 系统。
Redo Log:技术深度剖析(不聊枯燥部分)
可视化 InnoDB 内部的运行情况:
┌─────────────────────────────────────────────────────┐
│ Transaction Flow │
├─────────────────────────────────────────────────────┤
│ 1. Client: "UPDATE accounts..." │
│ │ │
│ ▼ │
│ 2. InnoDB: Write to REDO LOG (on disk) │
│ [Sequential write - fast! ⚡] │
│ │ │
│ ▼ │
│ 3. InnoDB: Update buffer pool (in RAM) │
│ [Random access - still fast! 💨] │
│ │ │
│ ▼ │
│ 4. Client: "COMMIT" │
│ │ │
│ ▼ │
│ 5. InnoDB: Flush redo to disk (fsync) │
│ [This is where we wait... 🐌] │
└─────────────────────────────────────────────────────┘
**关键洞察:**Redo 写入是顺序的(快),但 fsync 极其慢,尤其在高负载下。每秒 5,000 笔事务,每笔都需要磁盘刷新,这相当于让存储每秒执行 5,000 次同步操作——连 SSD 都受不了。
调查:改变一切的四个变量
1. innodb_flush_log_at_trx_commit
-- Safest, slowest
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
-- OS cache, faster
SET GLOBAL innodb_flush_log_at_trx_commit = 2;
-- Unsafe, fastest
SET GLOBAL innodb_flush_log_at_trx_commit = 0;
| 设置 | 持久性 | 性能 | 使用场景 |
|---|---|---|---|
| 1 | 完全 ACID | 最慢 | 金融数据 |
| 2 | 操作系统缓存 | 快 | Web 应用 |
| 0 | 有风险 | 最快 | 分析 |
我们改为 2,提升了 10 倍吞吐量。
2. innodb_log_file_size
Redo log 是一个循环缓冲区:
[Checkpoint] ---> ███████ write position ---> (wrap)
如果写指针追上检查点 → 停滞。
SHOW VARIABLES LIKE 'innodb_log_file_size';
我们把它从 48 MB 增大到 2 GB × 2 个文件 = 4 GB。
经验法则: 大小足以容纳 ≈ 1 小时的峰值写入。
3. innodb_flush_method
SET GLOBAL innodb_flush_method = 'O_DIRECT';
防止双重缓存,使我们的 I/O 等待降低了 30 %。
4. 批量提交
之前:
5000 commits/sec → 5000 fsync calls
批量后:
100 commits/sec → 100 fsync calls
**结果:**磁盘操作减少了 50 倍。
只读事务的秘密武器
START TRANSACTION READ ONLY;
SELECT SUM(balance)
FROM accounts
WHERE region = 'US-WEST';
COMMIT;
随后 InnoDB:
- 跳过 redo 日志记录
- 跳过锁定
- 跳过创建事务 ID
结果:对报表查询 ≈ 4 倍更快。
监控 Redo Log
SELECT
'Redo Log Usage' AS metric,
ROUND(
(SELECT VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_os_log_written') / 1024 / 1024,
2
) AS value
UNION ALL
SELECT
'Log Waits',
(SELECT VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_log_waits')
UNION ALL
SELECT
'Checkpoint Age',
ROUND(
(SELECT VARIABLE_VALUE
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_checkpoint_age') / 1024 / 1024,
2
);
如果 Innodb_log_waits > 0 → 说明你的 redo log 太小。
生产环境优化手册
-
优化前先进行剖析
SET GLOBAL slow_query_log = 1; SET GLOBAL long_query_time = 0.1; SET GLOBAL log_slow_extra = 1; -
为 Redo Log 设定合适大小
使用每日指标,将日志文件大小设为 ≈ 2 倍峰值小时。 -
通过基准测试验证持久性设置
对比innodb_flush_log_at_trx_commit为 1、2、0 时的表现。 -
使用只读事务 进行报表和分析。
经验教训
- Redo log 既是 你的安全网,也是 瓶颈。
- 每一种持久性保证都伴随性能代价。
- 日志文件大小至关重要。
- 只读事务被严重低估。
- 批量写入是大规模场景下最有效的优化之一。
进一步阅读
- MySQL InnoDB 文档:Redo Log
- Percona:《调优 InnoDB Redo 日志》
- 《High Performance MySQL》(O’Reilly)
Tags: #mysql #database #performance #devops