大家好!作为开发者,工作中无法避免会遇到消息中间件的使用,作为一名在消息中间件这个"泥坑"里摸爬滚打多年的开发者。
今天想和大家一起聊聊两大消息中间件巨头 RocketMQ 和 Kafka 在持久化方面的那些事儿。
持久化机制一直是高可用架构的命脉所在,值得每个开发者好好研究一番!
为什么持久化这么重要?
先问大家一个问题,假设你的消息系统突然崩溃了,数据会怎样?是灰飞烟灭还是死而复生?
没错,持久化就是解决这个问题的关键!它让我们的消息系统具备了"记忆"能力,即使遭遇重启、崩溃,消息数据依然安然无恙。
接下来,我们就一起揭开 RocketMQ 和 Kafka 持久化机制的神秘面纱,看看它们各自的绝技!
RocketMQ 的持久化机制
CommitLog + ConsumeQueue 的二级存储设计
RocketMQ 采用了一种非常聪明的二级存储结构,包括 CommitLog 和 ConsumeQueue 两部分。

这种存储结构很巧妙,让我们来拆解一下
-
CommitLog 是一种磁盘上的顺序写日志,所有的主题(Topic)消息都写在这一个文件里,实现了"写入永远是顺序的"这一高性能准则。
-
ConsumeQueue 则是一个逻辑消费队列,相当于 CommitLog 的索引文件,保存了指向 CommitLog 的物理偏移量,按照 Topic + MessageQueue 组织,方便消费者快速定位消息。
想象一下,CommitLog 就像一本流水账,记录所有的交易,而 ConsumeQueue 则像是一个分类目录,帮你快速找到特定类型的交易记录。
文件存储结构
RocketMQ 的文件保存在磁盘上是如何组织的呢?
/rocketmq_store
/commitlog
00000000000000000000 (1G文件大小)
00000000001073741824 (文件名代表起始偏移量)
00000000002147483648
...
/consumequeue
/topic_1
/0 (队列ID为0)
00000000000000000000 (6个字节/索引 × 30万索引)
00000000000006000000
...
/1 (队列ID为1)
00000000000000000000
...
/topic_2
/0
...
/index
/20230101 (索引文件按照日期命名)
/20230102
...
/config
consumerOffset.json (消费者组的消费位置)
delayOffset.json (延迟消费队列进度)
topics.json (主题配置信息)
subscriptionGroup.json (订阅组配置)
...
CommitLog 的特点
-
分片存储
每个文件大小固定为1GB,当一个文件写满后,会创建新文件继续写入。 -
文件命名
文件名使用20位十进制数字,表示当前文件的起始偏移量,如 00000000000000000000 表示从0开始的文件。 -
消息格式
每条消息包含消息长度、物理偏移量、消息体、主题、队列ID等信息。
看看一个典型的 RocketMQ 消息在 CommitLog 中的存储格式

ConsumeQueue 的结构
ConsumeQueue 文件包含若干条定长条目,每个条目固定20字节,由三部分组成
-
CommitLog 物理偏移量(8字节)
指向消息在 CommitLog 中的位置 -
消息长度(4字节)
消息在 CommitLog 中占用的字节数 -
消息 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;
}

最低0.47元/天 解锁文章
1353

被折叠的 条评论
为什么被折叠?



