文件刷盘机制
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在如下三种情况任意之一满足的情况下将继续执行删除文件操作。
- 指定删除文件的时间点,RocketMQ通过deleteWhen设置一天的固定时间执行一次删除过期文件操作,默认为凌晨4点
- 磁盘空间是否充足,如果磁盘空间不充足,则返回true,表示应该触发过期文件删除操作。
- 预留,手工触发,可以通过调用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小时)的消息,防止误删新数据。
- 即使磁盘空间不足,至少保留最近3小时(
与消费者进度的关系
-
独立删除策略:
- 消息删除不受消费者进度影响。即使存在消费者未消费的消息,只要超过保留时间或触发磁盘水位,依然会被删除。
- 潜在风险:消费者进度严重滞后时可能导致消息丢失。需通过监控消费积压并及时处理。
特殊场景处理
-
事务消息:
- 未提交的事务消息(状态为
PREPARED)在Broker重启时会触发回查,若未及时处理可能随过期文件被删除。
- 未提交的事务消息(状态为
-
定时消息:
- 定时消息在未到达投递时间时,即使超过
fileReservedTime也会保留,确保按时投递。
- 定时消息在未到达投递时间时,即使超过