这是《RocketMQ 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 CommitLog 是 RocketMQ Broker 的核心存储文件,所有 Topic 的消息都会顺序追加到同一个 CommitLog 中。理解 CommitLog,才能理解 RocketMQ 的写入性能、磁盘布局、文件回收和恢复机制。
14.1 存储全景
Producer
-> Broker
-> CommitLog 所有消息的物理正文
-> ConsumeQueue 每个队列的逻辑索引
-> IndexFile Key 和时间查询索引
-> Consumer
消息正文只存一份,ConsumeQueue 保存的是定位信息:
ConsumeQueue entry:
CommitLog offset
message size
tag hashcode
这种设计让写入集中在少量文件上,有利于顺序写和页缓存,同时仍能按队列消费。
14.2 文件布局
典型目录:
store/
commitlog/
00000000000000000000
00000000001073741824
00000000002147483648
consumequeue/
OrderTopic/
0/
1/
index/
20260825000000000
config/
checkpoint
CommitLog 文件名通常表示该文件起始偏移量。文件大小常见为 1GB,具体默认值随版本和配置变化。
14.3 写入流程
validate message
-> get or create MappedFile
-> append message to CommitLog
-> build ConsumeQueue and Index dispatch
-> flush according to policy
-> replicate according to role
关键组件:
| 组件 | 职责 |
|---|---|
| PutMessage 线程 | 接收并写入消息 |
| ReputMessageService | 构建 ConsumeQueue 和 Index |
| FlushCommitLogService | 刷盘 |
| GroupTransferService | 同步复制等待 |
| CleanCommitLogService | 过期文件回收 |
14.4 MappedFile 与页缓存
RocketMQ 使用内存映射文件写入 CommitLog。写入先进入页缓存,何时落盘取决于刷盘策略和操作系统回写机制。
风险:
- 宿主机断电可能丢失未刷盘数据;
- 页缓存污染会影响延迟;
- 大量冷读会挤占页缓存;
- 内存映射异常需要观察 Broker 日志和磁盘状态。
生产建议:
- 独立数据盘;
- 使用满足延迟和可靠性要求的文件系统;
- 关注
iowait、脏页和写入延迟; - 避免与日志、搜索索引混盘;
- 大规模冷读改走备份或对象存储。
14.5 消息大小与格式
CommitLog 中每条消息包含:
- 消息长度;
- Topic;
- properties;
- body;
- queueId;
- storeTime;
- 校验信息;
- 物理偏移量相关信息。
版本不同,具体编码和校验字段可能有差异。应用侧应避免构造超大消息,优先采用“小消息 + 对象存储引用”的模式。
14.6 文件过期与删除
保留策略通常由文件保留时间和磁盘水位控制:
if file is fully expired and disk usage below threshold:
delete file
elif disk usage exceeds threshold:
force clean old files or reject writes
治理要点:
- 保留时间要大于最长消费延迟和故障恢复窗口;
- 死信和重试 Topic 也要纳入容量;
- 磁盘高水位会触发告警或拒写;
- 不能在消费者长期落后时随意删除数据;
- 扩容前先评估磁盘增长速度。
14.7 恢复机制
Broker 启动时会校验并恢复存储:
read checkpoint
-> load commitlog files
-> validate message
-> recover consumequeue
-> rebuild dispatch if needed
异常断电后:
- 最后一笔未完整写入的消息可能被截断;
- 副本之间需要比较位点;
- Controller 或 DLedger 模式参与选主;
- 恢复时间与 CommitLog 数量和损坏程度相关。
不要在未确认文件副本和位点前手工删除“看起来异常”的存储文件。
14.8 性能观测
指标:
broker_put_requests_total
broker_put_latency_ms
commitlog_flush_latency_ms
commitlog_mapped_file_count
disk_usage_ratio
disk_write_iops
disk_write_throughput
page_cache_hit_ratio
dispatch_lag
异常判断:
| 现象 | 可能原因 |
|---|---|
| 写入延迟升高 | 磁盘瓶颈、刷盘慢、复制等待 |
| dispatch lag | Reput 线程积压 |
| 磁盘增长快 | 保留过长、消费落后、重试风暴 |
| 页缓存命中率下降 | 冷读过多 |
| 文件创建异常 | 磁盘满、权限、文件句柄 |
14.9 生产实践
- CommitLog 使用独立高性能盘;
- 根据可靠性选择刷盘和复制策略;
- 控制单条消息大小;
- 设置磁盘告警和拒写水位;
- 预估至少一个故障恢复周期的保留容量;
- 压测写入、消费、冷查混合场景;
- 定期演练 Broker 重启恢复;
- 备份关键 Topic 和配置。
本章小结
CommitLog 用“全局顺序追加 + 队列索引”的方式兼顾写入吞吐和队列消费模型。它决定了 RocketMQ 的磁盘形态、恢复逻辑和容量上限。生产环境要重点关注刷盘、复制、页缓存、文件回收和恢复时间,而不是只看发送 TPS。
思考题
- 为什么所有 Topic 共用 CommitLog 有利于顺序写?
- ConsumeQueue 保存的是消息正文还是索引?
- 异步刷盘在什么故障下可能丢数据?
- 保留时间为什么要大于消费延迟窗口?
- Broker 启动恢复时为什么要校验最后写入的数据?