简介
- Broker文件清理主要清理CommitLog、ConsumeQueue、IndexFile
- CommitLog清理规则
- 文件过期(默认72小时),且达到清理时间点(默认是凌晨4点),删除过期文件
- 文件过期(默认72小时),且磁盘空间达到了75%(默认),删除过期文件
- 磁盘已经达到上限(默认85%)的时候,则开始批量清理文件(无论是否过期),直到空间充足
- 只删除到倒数第二个文件,不删除最后一个文件
- 若磁盘空间达到危险水位线(默认90%),出于保护自身的目的,broker会拒绝写入服务
- 清理CommitLog并不是一条一条的删除,而是对比MappedFile最后一条消息是否还在实效范围内,如果是则不会被清理,否则会被清理。除非当磁盘占用85%时,此时无论是否过期,会理解删除。清理完CommitLog后,获取到CommitLog最小的偏移量offset,然后将ConsumeQueue和IndexFile中最小的offset删除掉(同样也是删除文件)。
源码分析
-
DefaultMessageStore#start
启动时,会添加一些定时任务(调用DefaultMessageStore#addScheduleTask
方法),其中有一个定时任务就是清理文件的。默认初始延迟60s,每10s执行一次this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { DefaultMessageStore.this.cleanFilesPeriodically(); } }, 1000 * 60, this.messageStoreConfig.getCleanResourceInterval(), TimeUnit.MILLISECONDS); private void cleanFilesPeriodically() { // 清理CommitLog this.cleanCommitLogService.run(); // 清理ConsumeQueue和IndexFile this.cleanConsumeQueueService.run(); }
CommitLog清理
-
CleanCommitLogService
用于清理CommitLog文件。此类的run方式被删除文件定时任务调用public void run() { try { //删除过期文件 this.deleteExpiredFiles(); // 重删挂起的文件,线程引用过期文件、内存映射清理失败,都可能导致删除失败 // 判断第一个MappedFile是否可用 // 可能上面的MappedFile销毁失败,只是设置了不可用,但是并没有销毁,此处重删。但是这里只是删除第一个? this.redeleteHangedFile(); } catch (Throwable e) { DefaultMessageStore.log.warn(this.getServiceName() + " service has exception. ", e); } }
-
CleanCommitLogService#deleteExpiredFiles
- 文件保留时间,默认72小时,多个文件默认删除间隔100ms(为了避免影响磁盘性能)
- 删除的触发条件有三个:到了删除时间、磁盘满了、手动删除(目前没看到哪里使用)
private void deleteExpiredFiles() { int deleteCount = 0; //文件保留时间,默认72小时 long fileReservedTime = DefaultMessageStore.this.getMessageStoreConfig().getFileReservedTime(); //删除的时间间隔,默认100ms int deletePhysicFilesInterval = DefaultMessageStore.this.getMessageStoreConfig().getDeleteCommitLogFilesInterval(); // int destroyMapedFileIntervalForcibly = DefaultMessageStore.this.getMessageStoreConfig().getDestroyMapedFileIntervalForcibly(); //是否到删除时间,默认是04,凌晨4点 boolean timeup = this.isTimeToDelete(); //磁盘空间是否满了 boolean spacefull = this.isSpaceToDelete(); //TODO 手动删除文件次数是否大于0,目前没看到哪里使用到 boolean manualDelete = this.manualDeleteFileSeveralTimes > 0; if (timeup || spacefull || manualDelete) { if (manualDelete) this.manualDeleteFileSeveralTimes--; // 开启强制清理(默认true)&& 立即清理 boolean cleanAtOnce = DefaultMessageStore.this.getMessageStoreConfig().isCleanFileForciblyEnable() && this.cleanImmediately; log.info("begin to delete before {} hours file. timeup: {} spacefull: {} manualDeleteFileSeveralTimes: {} cleanAtOnce: {}", fileReservedTime, timeup, spacefull, manualDeleteFileSeveralTimes, cleanAtOnce); //过期时间默认是72小时,如果一个文件commitLog的数据文件在72小时内没有被修改过 那么就认为该文件已经过期了 fileReservedTime *= 60 * 60 * 1000; //删除文件,通过mappedFileQueue来删除 deleteCount = DefaultMessageStore.this.commitLog.deleteExpiredFile(fileReservedTime, deletePhysicFilesInterval, destroyMapedFileIntervalForcibly, cleanAtOnce); if (deleteCount > 0) { } else if (spacefull) { log.warn("disk space will be full soon, but delete file failed."); } } }
-
判断是否到时间,默认是凌晨四点,判断小时是否是当前小时
private boolean isTimeToDelete() { // 默认是04,凌晨4点。多个时间使用;分号分隔 String when = DefaultMessageStore.this.getMessageStoreConfig().getDeleteWhen(); if (UtilAll.isItTimeToDo(when)) { DefaultMessageStore.log.info("it's time to reclaim disk space, " + when); return true; } return false; }
-
判断磁盘空间是否满了:磁盘占比配置,默认是75%,如果配置小于10,则按照10。如果配置大于95则按照95。大于75%,则返回磁盘空间满的状态。如果磁盘使用率大于85%,设置立即清理状态为true,表示无论是否72小时过期,都会删除。
private final double diskSpaceWarningLevelRatio = Double.parseDouble(System.getProperty("rocketmq.broker.diskSpaceWarningLevelRatio", "0.90")); private final double diskSpaceCleanForciblyRatio = Double.parseDouble(System.getProperty("rocketmq.broker.diskSpaceCleanForciblyRatio", "0.85")); private boolean isSpaceToDelete() { // 磁盘空间占比默认是0.75,如果配置<10,按照10,如果配置>95,按照95 double ratio = DefaultMessageStore.this.getMessageStoreConfig().getDiskMaxUsedSpaceRatio() / 100.0; cleanImmediately = false; { // CommitLog路径 String storePathPhysic = DefaultMessageStore.this.getMessageStoreConfig().getStorePathCommitLog(); // 返回此磁盘分区使用的占比 double physicRatio = UtilAll.getDiskPartitionSpaceUsedPercent(storePathPhysic); // 如果磁盘使用率大于90%,就设置runningFlags标志位为磁盘满了的状态 if (physicRatio > diskSpaceWarningLevelRatio) { boolean diskok = DefaultMessageStore.this.runningFlags.getAndMakeDiskFull(); if (diskok) { DefaultMessageStore.log.error("physic disk maybe full soon " + physicRatio <