RocketMQNotes

第 14 章:CommitLog

zjc 于 2026-01-14 发布

这是《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。写入先进入页缓存,何时落盘取决于刷盘策略和操作系统回写机制。

风险:

  1. 宿主机断电可能丢失未刷盘数据;
  2. 页缓存污染会影响延迟;
  3. 大量冷读会挤占页缓存;
  4. 内存映射异常需要观察 Broker 日志和磁盘状态。

生产建议:

  1. 独立数据盘;
  2. 使用满足延迟和可靠性要求的文件系统;
  3. 关注 iowait、脏页和写入延迟;
  4. 避免与日志、搜索索引混盘;
  5. 大规模冷读改走备份或对象存储。

14.5 消息大小与格式

CommitLog 中每条消息包含:

  1. 消息长度;
  2. Topic;
  3. properties;
  4. body;
  5. queueId;
  6. storeTime;
  7. 校验信息;
  8. 物理偏移量相关信息。

版本不同,具体编码和校验字段可能有差异。应用侧应避免构造超大消息,优先采用“小消息 + 对象存储引用”的模式。

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

治理要点:

  1. 保留时间要大于最长消费延迟和故障恢复窗口;
  2. 死信和重试 Topic 也要纳入容量;
  3. 磁盘高水位会触发告警或拒写;
  4. 不能在消费者长期落后时随意删除数据;
  5. 扩容前先评估磁盘增长速度。

14.7 恢复机制

Broker 启动时会校验并恢复存储:

read checkpoint
  -> load commitlog files
  -> validate message
  -> recover consumequeue
  -> rebuild dispatch if needed

异常断电后:

  1. 最后一笔未完整写入的消息可能被截断;
  2. 副本之间需要比较位点;
  3. Controller 或 DLedger 模式参与选主;
  4. 恢复时间与 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 生产实践

  1. CommitLog 使用独立高性能盘;
  2. 根据可靠性选择刷盘和复制策略;
  3. 控制单条消息大小;
  4. 设置磁盘告警和拒写水位;
  5. 预估至少一个故障恢复周期的保留容量;
  6. 压测写入、消费、冷查混合场景;
  7. 定期演练 Broker 重启恢复;
  8. 备份关键 Topic 和配置。

本章小结

CommitLog 用“全局顺序追加 + 队列索引”的方式兼顾写入吞吐和队列消费模型。它决定了 RocketMQ 的磁盘形态、恢复逻辑和容量上限。生产环境要重点关注刷盘、复制、页缓存、文件回收和恢复时间,而不是只看发送 TPS。

思考题

  1. 为什么所有 Topic 共用 CommitLog 有利于顺序写?
  2. ConsumeQueue 保存的是消息正文还是索引?
  3. 异步刷盘在什么故障下可能丢数据?
  4. 保留时间为什么要大于消费延迟窗口?
  5. Broker 启动恢复时为什么要校验最后写入的数据?