深入浅出 RocketMQ 与 Kafka 的持久化机制全解析

大家好!作为开发者,工作中无法避免会遇到消息中间件的使用,作为一名在消息中间件这个"泥坑"里摸爬滚打多年的开发者。

今天想和大家一起聊聊两大消息中间件巨头 RocketMQ 和 Kafka 在持久化方面的那些事儿。

持久化机制一直是高可用架构的命脉所在,值得每个开发者好好研究一番!

为什么持久化这么重要?

先问大家一个问题,假设你的消息系统突然崩溃了,数据会怎样?是灰飞烟灭还是死而复生?

没错,持久化就是解决这个问题的关键!它让我们的消息系统具备了"记忆"能力,即使遭遇重启、崩溃,消息数据依然安然无恙。

接下来,我们就一起揭开 RocketMQ 和 Kafka 持久化机制的神秘面纱,看看它们各自的绝技!

RocketMQ 的持久化机制

CommitLog + ConsumeQueue 的二级存储设计

RocketMQ 采用了一种非常聪明的二级存储结构,包括 CommitLog 和 ConsumeQueue 两部分。

在这里插入图片描述

这种存储结构很巧妙,让我们来拆解一下

  1. CommitLog 是一种磁盘上的顺序写日志,所有的主题(Topic)消息都写在这一个文件里,实现了"写入永远是顺序的"这一高性能准则。

  2. ConsumeQueue 则是一个逻辑消费队列,相当于 CommitLog 的索引文件,保存了指向 CommitLog 的物理偏移量,按照 Topic + MessageQueue 组织,方便消费者快速定位消息。

想象一下,CommitLog 就像一本流水账,记录所有的交易,而 ConsumeQueue 则像是一个分类目录,帮你快速找到特定类型的交易记录。

文件存储结构

RocketMQ 的文件保存在磁盘上是如何组织的呢?

/rocketmq_store
  /commitlog
    00000000000000000000  (1G文件大小)
    00000000001073741824  (文件名代表起始偏移量)
    00000000002147483648
    ...
  /consumequeue
    /topic_1
      /0   (队列ID0)
        00000000000000000000  (6个字节/索引 × 30万索引)
        00000000000006000000
        ...
      /1   (队列ID1)
        00000000000000000000
        ...
    /topic_2
      /0
        ...
  /index
    /20230101  (索引文件按照日期命名)
    /20230102
    ...
  /config
    consumerOffset.json  (消费者组的消费位置)
    delayOffset.json     (延迟消费队列进度)
    topics.json          (主题配置信息)
    subscriptionGroup.json (订阅组配置)
    ...
CommitLog 的特点
  1. 分片存储
    每个文件大小固定为1GB,当一个文件写满后,会创建新文件继续写入。

  2. 文件命名
    文件名使用20位十进制数字,表示当前文件的起始偏移量,如 00000000000000000000 表示从0开始的文件。

  3. 消息格式
    每条消息包含消息长度、物理偏移量、消息体、主题、队列ID等信息。

看看一个典型的 RocketMQ 消息在 CommitLog 中的存储格式

在这里插入图片描述

ConsumeQueue 的结构

ConsumeQueue 文件包含若干条定长条目,每个条目固定20字节,由三部分组成

  1. CommitLog 物理偏移量(8字节)
    指向消息在 CommitLog 中的位置

  2. 消息长度(4字节)
    消息在 CommitLog 中占用的字节数

  3. 消息 Tag 的哈希码(8字节)
    用于消息过滤

看起来有点抽象?没关系,我们用代码来理解一下 RocketMQ 存储时的关键流程

// 简化版的消息存储过程,展示核心逻辑
public class RocketMQStorageSimulator {
   
   
    
    // 模拟CommitLog将消息追加到日志文件
    public PutMessageResult appendToCommitLog(Message msg) {
   
   
        // 1. 获取CommitLog文件,如果当前文件已满则创建新文件
        MappedFile mappedFile = getAvailableMappedFile();
        
        // 2. 计算消息长度并检查剩余空间
        int msgLen = calculateMessageLength(msg);
        if (mappedFile.getFileSize() - mappedFile.getWritePosition() < msgLen) {
   
   
            // 当前文件不足以容纳消息,需要创建新文件
            mappedFile = createNewMappedFile();
        }
        
        // 3. 构建消息存储格式
        ByteBuffer buffer = buildMessageBuffer(msg, msgLen);
        
        // 4. 追加写入CommitLog文件
        long offset = mappedFile.getFileFromOffset() + mappedFile.getWritePosition();
        mappedFile.appendMessage(buffer);
        
        // 5. 构建返回结果包含消息在CommitLog中的物理偏移量
        PutMessageResult result = new PutMessageResult(PutMessageStatus.OK, offset, msgLen);
        
        // 6. 返回结果,供后续更新ConsumeQueue索引用
        return result;
    }
    
    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慢德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值