RocketMq存储篇(刷盘机制、删除机制)

460 阅读6分钟

文件刷盘机制

RocketMQ的存储与读写是基于JDK NIO的内存映射机制(MappedByteBuffer)的,消息存储时首先将消息追加到内存,再根据配置的刷盘策略在不同时间进行刷写磁盘。

如果是同步刷盘,消息追加到内存后,将同步调用MappedByteBuffer的force()方法;如果是异步刷盘,在消息追加到内存后立刻返回给消息发送端。

RocketMQ使用一个单独的线程按照某一个设定的频率执行刷盘操作。通过在broker配置文件中配置flushDiskType来设定刷盘方式,可选值为ASYNC_FLUSH(异步刷盘)、SYNC_FLUSH(同步刷盘)​,默认为异步刷盘。值得注意的是索引文件的刷盘并不是采取定时刷盘机制,而是每更新一次索引文件就会将上一次的改动刷写到磁盘。

Broker同步刷盘

同步刷盘,指的是在消息追加到内存映射文件的内存中后,立即将数据从内存刷写到磁盘文件,由CommitLog的handleDiskFlush方法实现。

消费发送线程将消息追加到内存映射文件后,将同步任务GroupCommitRequest提交到GroupCommitService线程,然后调用阻塞等待刷盘结果,超时时间默认为5s。

处理完所有同步刷盘任务后,更新刷盘检测点StoreCheckpoint中的physicMsg-Timestamp,但并没有执行检测点的刷盘操作,刷盘检测点的刷盘操作将在刷写消息队列文件时触发。(checkPoint文件)

Broker的异步刷盘

异步刷盘根据是否开启transientStorePoolEnable机制,刷盘实现会有细微差别。

如果transientStorePoolEnable为true, RocketMQ会单独申请一个与目标物理文件(commitlog)同样大小的堆外内存,该堆外内存将使用内存锁定,确保不会被置换到虚拟内存中去,消息首先追加到堆外内存,然后提交到与物理文件的内存映射内存中,再flush到磁盘。如果transientStorePoolEnable为flalse,消息直接追加到与物理文件直接映射的内存中,然后刷写到磁盘中。

调用flush方法将内存中数据刷写到磁盘,并且更新存储检测点文件的commitlog文件的更新时间戳,文件检测点文件(checkpoint文件)的刷盘动作在刷盘消息消费队列线程中执行,其入口为DefaultMessageStore#FlushConsumeQueueService。

过期文件删除

由于RocketMQ操作CommitLog、ConsumeQueue文件是基于内存映射机制并在启动的时候会加载commitlog、ConsumeQueue目录下的所有文件,为了避免内存与磁盘的浪费,不可能将消息永久存储在消息服务器上,所以需要引入一种机制来删除已过期的文件。

RocketMQ顺序写Commitlog文件、ConsumeQueue文件,所有写操作全部落在最后一个CommitLog或ConsumeQueue文件上,之前的文件在下一个文件创建后将不会再被更新。RocketMQ清除过期文件的方法是:如果非当前写文件在一定时间间隔内没有再次被更新,则认为是过期文件,可以被删除,RocketMQ不会关注这个文件上的消息是否全部被消费。

默认每个文件的过期时间为72小时,通过在Broker配置文件中设置fileReservedTime来改变过期时间,单位为小时。

Step1: RocketMQ会每隔10s调度一次cleanFilesPeriodically,检测是否需要清除过期文件。执行频率可以通过设置cleanResourceInterval,默认为10s

Step2: 分别执行清除消息存储文件(Commitlog文件)与消息消费队列文件(ConsumeQueue文件)​。消息消费队列文件与消息存储文件(Commitlog)共用一套过期文件删除机制.

Step3:RocketMQ在如下三种情况任意之一满足的情况下将继续执行删除文件操作。

  1. 指定删除文件的时间点,RocketMQ通过deleteWhen设置一天的固定时间执行一次删除过期文件操作,默认为凌晨4点
  2. 磁盘空间是否充足,如果磁盘空间不充足,则返回true,表示应该触发过期文件删除操作。
  3. 预留,手工触发,可以通过调用excuteDeleteFilesManualy方法手工触发过期文件删除,目前RocketMQ暂未封装手工触发文件删除的命令。

总结

基于时间的消息删除(基于控制台可以删除单条消息)

  • 默认保留策略

    • RocketMQ默认保留消息3天,超过此时限的CommitLog文件会被自动删除。
    • 配置参数:通过fileReservedTime(单位:小时)调整保留时间,例如设置为72表示3天。
  • 删除触发条件

    • 定时扫描:后台线程DefaultMessageStore.CleanCommitLogService每10分钟(可配置)检查一次文件过期情况。
    • 文件选择:删除所有最后修改时间早于当前时间 - fileReservedTime的CommitLog文件

基于磁盘空间的动态删除

  • 磁盘水位控制

    • 当磁盘剩余空间低于阈值(默认diskMaxUsedSpaceRatio=75%)时,触发紧急删除。

    • 配置参数

      • diskSpaceWarningLevelRatio=70%:警告水位,日志提示但暂不删除。
      • diskSpaceCleanForciblyRatio=85%:强制删除水位,立即删除旧文件。
  • 删除策略

    • 按CommitLog文件的创建时间排序,优先删除最旧的文件,直至磁盘使用率降至阈值以下。

删除实现细节

(1) 文件类型与删除顺序

  • CommitLog文件

    • 消息的物理存储文件,删除时需确保无消费者或索引依赖。
    • 文件名基于起始偏移量(如00000000000000000000),删除时按时间顺序选择最旧文件。
  • ConsumerQueue文件

    • 逻辑队列索引文件,随CommitLog删除同步清理,避免无效索引。
  • Index文件

    • 哈希索引文件,删除后可通过CommitLog重建(但影响查询性能)。

(2) 删除安全机制

  • 引用检查

    • 删除CommitLog文件前,确认所有ConsumerQueue的最大偏移量均超过该文件的最大偏移量,确保无业务依赖。
  • 强制保留时间

    • 即使磁盘空间不足,至少保留最近3小时(fileReservedTime最小值为72小时)的消息,防止误删新数据。

与消费者进度的关系

  • 独立删除策略

    • 消息删除不受消费者进度影响。即使存在消费者未消费的消息,只要超过保留时间或触发磁盘水位,依然会被删除。
    • 潜在风险:消费者进度严重滞后时可能导致消息丢失。需通过监控消费积压并及时处理。

特殊场景处理

  • 事务消息

    • 未提交的事务消息(状态为PREPARED)在Broker重启时会触发回查,若未及时处理可能随过期文件被删除。
  • 定时消息

    • 定时消息在未到达投递时间时,即使超过fileReservedTime也会保留,确保按时投递。