RocketMQ消息存储三:逻辑对象层CommitLog

概述

CommitLog中存储了消息的完整信息,所有topic的消息都存储在一个CommitLog中。下面讲一下CommitLog的存储格式和存储过程

存储文件信息

CommtLog的存储其实是分多层的,CommitLog -> MappedFileQueue -> MappedFile,其中真正存储数据的是MappedFile。CommitLog存储目录为$HOME\store\commitlog${fileName},每个文件大小为1G,不满则填充空白。每个文件名字长度为20,具体命名为文件偏移量,左边补0。如第一个文件为00000000000000000000,第二个文件为00000000001073741824。

CommitLog消息格式

在这里插入图片描述

关键属性

MappedFileQueue

CommitLog存储在多个文件中,多个文件用MappedFileQueue来维护。MappedFileQueue是MappedFile的集合,用来管理MappedFile队列,提供了查找、定位、删除等功能。其属性mappedFiles为CopyOnWriteArrayList。
下面看具体方法。

checkSelf()自检

    public void checkSelf() {
   
        if (!this.mappedFiles.isEmpty()) {
   
            Iterator<MappedFile> iterator = mappedFiles.iterator();
            MappedFile pre = null;
            // 遍历所有mappedFile
            while (iterator.hasNext()) {
   
                MappedFile cur = iterator.next();
                if (pre != null) {
   
                    // 校验相邻两个文件offset差值是否为文件大小
                    if (cur.getFileFromOffset() - pre.getFileFromOffset() != this.mappedFileSize) {
   
                        LOG_ERROR.error("[BUG]The mappedFile queue's data is damaged, the adjacent mappedFile's offset don't match. pre file {}, cur file {}",
                            pre.getFileName(), cur.getFileName());
                    }
                }
                pre = cur;
            }
        }
    }

getMappedFileByTime()时间戳查找文件

按文件最后更新时间查找。这里的文件生成后不会再更新,所以队列中文件的最后修改时间是递增的。

truncateDirtyFiles()清理脏文件

清理大于指定offset的文件

删除过期文件

按照不同方式删除,看源码即可

getLastMappedFile(final long startOffset, boolean needCreate)获取最后一个文件

/**
     * 三种情况
     * 1、mappedFiles为空,如果需要则创建新文件
     * 2、如果最后一个文件满了,如果需要则创建新文件
     * 3、返回最后一个文件
     */
    public MappedFile getLastMappedFile(final long startOffset, boolean needCreate) {
   
        long createOffset = -1;
        MappedFile mappedFileLast = getLastMappedFile();
        // mappedFiles为空
        if (mappedFileLast == null) {
   
            createOffset = startOffset - (startOffset % this.mappedFileSize);
        }
        // 最后一个文件满了
        if (mappedFileLast != null && mappedFileLast.isFull()) {
   
            createOffset = mappedFileLast.getFileFromOffset() + this.mappedFileSize;
        }
        // 创建文件
        if (createOffset != -1 && needCreate) {
   
            String nextFilePath = this.storePath + File.separator + UtilAll.offset2FileName(createOffset);
            String nextNextFilePath = this.storePath + File.separator
                + UtilAll.offset2FileName(createOffset + this.mappedFileSize);
            MappedFile mappedFile = null;

            if (this.allocateMappedFileService != null) {
   
                mappedFile = this.allocateMappedFileService.putRequestAndReturnMappedFile(nextFilePath,
                    nextNextFilePath, this.mappedFileSize);
            } else {
   
                try {
   
                    mappedFile = new MappedFile(nextFilePath, this.mappedFileSize);
                } catch (IOException e) {
   
                    log.error("create mappedFile exception", e);
                }
            }
            if (mappedFile != null) {
   
                if (this.mappedFiles.isEmpty()) {
   
                    mappedFile.setFirstCreateInQueue(true);
                }
                this.mappedFiles.add(mappedFile);
            }
            return mappedFile;
        }

        return mappedFileLast;
    }

flush(int flushLeastPages)刷盘

    public boolean flush(final int flushLeastPages) {
   
        boolean result = true;
        // 上一次刷盘位置获取文件
        MappedFile mappedFile = this.findMappedFileByOffset(this.flushedWhere, this.flushedWhere == 0);
        if (mappedFile != null) {
   
            long tmpTimeStamp = mappedFile.getStoreTimestamp();
            // 交由mappedFile刷盘
            int offset = mappedFile.flush(flushLeastPages);
            long where = mappedFile.getFileFromOffset() + offset;
            // false表示有刷盘操作,true表示无变化
            result = where == this.flushedWhere;
            this.flushedWhere = where;
            if (0 == flushLeastPages) {
   
                this.storeTimestamp = tmpTimeStamp;
            }
        }

        return result;
    }

commit(int commitLeastPages)提交数据

public boolean commit(final int commitLeastPages) {
   
        boolean result = true;
        // 上一次提交位置获取文件
        MappedFile mappedFile = this.findMappedFileByOffset(this.committedWhere, this.committedWhere == 0);
        if (mappedFile != null) {
   
            // 交由mappedFile提交
            int offset = mappedFile.commit(commitLeastPages);
            long where = mappedFile.getFileFromOffset() + offset;
            // false表示有提交操作,true表示无变化
            result = where == this.committedWhere;
            this.committedWhere = where;
        }

        return result;
    }

其他查询相关不再详细陈述,具体看源码。

flushCommitLogService属性

刷盘服务,分为同步刷盘和异步刷盘,具体看配置。同步刷盘服务为GroupCommitService,异步刷盘服务为FlushRealTimeService,后面详细分析。

commitLogService属性

暂存数据提交服务,对应CommitRealTimeService,后面详细讲。

具体方法

flush

交由mappedFileQueue执行。

    public long flush() {
   
        this.mappedFileQueue.commit(0);
        
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值