RocketMQ 三大存储架构深度解析

102 阅读6分钟

RocketMQ 三大存储架构深度解析

从原理到实践:深入理解 CommitLog、ConsumeQueue 与 IndexFile

存储架构概述

RocketMQ 作为一款高性能的分布式消息中间件,其强大的消息存储能力是其核心竞争力之一。RocketMQ 采用了独特的三大存储结构来保证消息的高效写入、快速检索和可靠持久化。

重点: 三大核心存储组件

CommitLog ConsumeQueue IndexFile

这三个存储组件相互配合,形成了 RocketMQ 高效、可靠的消息存储体系。CommitLog 负责顺序写入所有消息,ConsumeQueue 提供消费者快速定位能力,IndexFile 则提供基于 Key 的消息检索功能。

三大核心存储组件

RocketMQ 整体存储架构

graph TB
    subgraph Producer["生产者"]
        P[Producer发送消息]
    end
    
    subgraph Broker["Broker存储系统"]
        direction TB
        CL[CommitLog顺序写入所有消息]
        CQ[ConsumeQueue消费队列索引]
        IF[IndexFile消息Key索引]
        
        CL -->|异步构建| CQ
        CL -->|异步构建| IF
    end
    
    subgraph Consumer["消费者"]
        C[Consumer拉取消息]
    end
    
    P -->|1. 写入消息| CL
    CQ -->|2. 定位消息| C
    CL -->|3. 读取消息体| C
    IF -.->|按Key查询| C
    
    style CL fill:#ff6b6b,stroke:#c92a2a,color:#fff
    style CQ fill:#51cf66,stroke:#2f9e44,color:#fff
    style IF fill:#339af0,stroke:#1864ab,color:#fff

CommitLog:消息的真实存储

核心概念

CommitLog 是 RocketMQ 最核心的存储文件,所有 Topic 的所有消息都按照到达的先后顺序写入到同一个 CommitLog 文件中。这种设计带来了极高的写入性能。

CommitLog 存储结构

graph LR
    subgraph CommitLog["CommitLog 文件序列"]
        direction LR
        F1["文件1000000000000000000001GB"]
        F2["文件2000000000010737418241GB"]
        F3["文件3000000000021474836481GB"]
        F4["当前写入00000000003221225472"]
    end
    
    F1 -->|写满| F2
    F2 -->|写满| F3
    F3 -->|写满| F4
    
    subgraph Message["单条消息结构"]
        direction TB
        M1[消息总长度 4B]
        M2[消息体长度 4B]
        M3[物理Offset 8B]
        M4[消息体 Body]
        M5[Topic]
        M6[Queue ID]
        M7[Properties]
    end
    
    F4 -.->|包含多条| Message
    
    style F4 fill:#ff6b6b,stroke:#c92a2a,color:#fff
    style Message fill:#fff5f5,stroke:#c92a2a

消息写入流程

sequenceDiagram
    participant P as Producer
    participant B as Broker
    participant ML as MappedFile(CommitLog)
    participant D as Disk
    
    P->>B: 1. 发送消息
    B->>B: 2. 设置存储时间戳
    B->>B: 3. 计算消息CRC校验
    B->>ML: 4. 获取最新MappedFile
    
    alt 文件未满
        ML->>ML: 5a. 追加写入消息
    else 文件已满
        ML->>ML: 5b. 创建新文件
        ML->>ML: 5c. 追加写入消息
    end
    
    alt 同步刷盘
        ML->>D: 6a. 立即刷盘
        D-->>ML: 6b. 刷盘完成
    else 异步刷盘
        ML-->>D: 6a. 后台异步刷盘
    end
    
    ML-->>B: 7. 返回写入结果
    B-->>P: 8. 返回发送结果

重点: 刷盘策略对比

策略 优点 缺点 适用场景

同步刷盘 数据可靠性高,不会丢失消息 性能较低,每次都要等待IO完成 对消息可靠性要求极高的场景

异步刷盘 性能高,吞吐量大 可能丢失少量数据(机器宕机) 对性能要求高,可容忍少量数据丢失

刷盘策略对比

ConsumeQueue:消费索引

核心概念

ConsumeQueue 是消息消费的逻辑队列,它保存了指定 Topic 下某个 Queue 的消息在 CommitLog 中的物理位置偏移量。消费者通过 ConsumeQueue 可以快速定位到要消费的消息。

为什么需要 ConsumeQueue?因为 CommitLog 中混合存储了所有 Topic 的消息,如果消费者直接扫描 CommitLog 来查找某个 Topic 的消息,效率会非常低。ConsumeQueue 作为索引,可以快速定位到特定 Topic 和 Queue 的消息。

CommitLog 与 ConsumeQueue 的映射关系

graph TB
    subgraph CL["CommitLog(混合存储)"]
        direction TB
        M1["消息1: Topic1-Queue0"]
        M2["消息2: Topic1-Queue1"]
        M3["消息3: Topic2-Queue0"]
        M4["消息4: Topic1-Queue0"]
        M5["消息5: Topic1-Queue1"]
        M6["消息6: Topic2-Queue0"]
        
        M1 --> M2 --> M3 --> M4 --> M5 --> M6
    end
    
    subgraph CQ1["Topic1-Queue0ConsumeQueue"]
        direction LR
        I1["Index1→M1"]
        I2["Index2→M4"]
        I1 --> I2
    end
    
    subgraph CQ2["Topic1-Queue1ConsumeQueue"]
        direction LR
        I3["Index1→M2"]
        I4["Index2→M5"]
        I3 --> I4
    end
    
    subgraph CQ3["Topic2-Queue0ConsumeQueue"]
        direction LR
        I5["Index1→M3"]
        I6["Index2→M6"]
        I5 --> I6
    end
    
    M1 -.->|异步构建| I1
    M4 -.->|异步构建| I2
    M2 -.->|异步构建| I3
    M5 -.->|异步构建| I4
    M3 -.->|异步构建| I5
    M6 -.->|异步构建| I6
    
    style CL fill:#ff6b6b,stroke:#c92a2a,color:#fff
    style CQ1 fill:#51cf66,stroke:#2f9e44,color:#fff
    style CQ2 fill:#51cf66,stroke:#2f9e44,color:#fff
    style CQ3 fill:#51cf66,stroke:#2f9e44,color:#fff

ConsumeQueue 条目结构

每个 ConsumeQueue 文件由多个固定大小的条目组成,每个条目占用 20 字节:

graph LR
    subgraph Entry["ConsumeQueue 单个条目(20字节)"]
        direction LR
        E1["CommitLog物理偏移量8字节"]
        E2["消息大小4字节"]
        E3["Tag HashCode8字节"]
    end
    
    E1 --> E2 --> E3
    
    style Entry fill:#e3f2fd,stroke:#1976d2

重点: ConsumeQueue 特点

轻量级索引:每条记录仅 20 字节,可以存储海量索引 顺序读取:消费者按顺序读取 ConsumeQueue,再根据偏移量到 CommitLog 读取完整消息 独立文件:每个 Topic 的每个 Queue 都有独立的 ConsumeQueue 文件 异步构建:由 ReputMessageService 异步从 CommitLog 构建

ConsumeQueue 特点

消息消费流程

sequenceDiagram
    participant C as Consumer
    participant B as Broker
    participant CQ as ConsumeQueue
    participant CL as CommitLog
    
    C->>B: 1. 拉取消息请求(Topic, Queue, Offset)
    B->>CQ: 2. 根据Offset读取ConsumeQueue
    CQ-->>B: 3. 返回消息索引(物理偏移量, 大小)
    B->>CL: 4. 根据物理偏移量读取CommitLog
    CL-->>B: 5. 返回完整消息
    B->>B: 6. 过滤消息(Tag匹配)
    B-->>C: 7. 返回消息给消费者
    C->>C: 8. 处理消息
    C->>B: 9. 提交消费进度

🔍 IndexFile:消息检索索引

核心概念

IndexFile 提供了基于消息 Key 的索引功能,允许用户根据消息的 Key 快速查询到消息。这对于消息追踪、问题排查等场景非常有用。

IndexFile 文件结构

graph TB
    subgraph IF["IndexFile 结构"]
        direction TB
        
        subgraph Header["Header(40字节)"]
            H1[beginTimestamp - 起始时间戳]
            H2[endTimestamp - 结束时间戳]
            H3[indexCount - 索引数量]
            H4[hashSlotCount - 哈希槽数量]
        end
        
        subgraph Slot["Slot Table(500W * 4字节)"]
            S1[Slot 0]
            S2[Slot 1]
            S3[...]
            S4[Slot 499999]
        end
        
        subgraph Index["Index Linked List(2000W * 20字节)"]
            I1["Index 1KeyHash | PhyOffsetTimeDiff | NextIndex"]
            I2["Index 2..."]
            I3["Index N..."]
        end
        
        Header --> Slot
        Slot --> Index
        S1 -.->|指向链表头| I1
        I1 -.->|next| I2
        I2 -.->|next| I3
    end
    
    style Header fill:#ffd43b,stroke:#f08c00
    style Slot fill:#a5d8ff,stroke:#1971c2
    style Index fill:#d0bfff,stroke:#7950f2

IndexFile 工作原理

IndexFile 采用了哈希索引 + 链表的数据结构:

graph LR
    subgraph Process["消息Key索引过程"]
        direction TB
        K["消息Key:ORDER_12345"]
        H["Hash计算:hash(KEY) % 500W"]
        S["Slot Table:定位到槽位"]
        L["Index链表:遍历查找"]
        R["返回消息:物理偏移量"]
        
        K --> H --> S --> L --> R
    end
    
    subgraph Detail["哈希冲突处理"]
        direction TB
        D1["不同Key可能映射到同一槽位"]
        D2["使用链表法解决冲突"]
        D3["新Index插入链表头部"]
        
        D1 --> D2 --> D3
    end
    
    Process -.->|详细说明| Detail
    
    style Process fill:#e3f2fd,stroke:#1976d2
    style Detail fill:#fff3e0,stroke:#f57c00

单个索引条目结构(20字节)

消息Key查询流程

sequenceDiagram
    participant U as User/Admin
    participant B as Broker
    participant IF as IndexFile
    participant CL as CommitLog
    
    U->>B: 1. 根据Key查询消息(Key: ORDER_12345)
    B->>IF: 2. 计算Key的Hash值
    IF->>IF: 3. 定位到Slot槽位
    IF->>IF: 4. 遍历链表查找匹配的Index
    IF-->>B: 5. 返回物理偏移量列表
    
    loop 每个偏移量
        B->>CL: 6. 根据物理偏移量读取消息
        CL-->>B: 7. 返回消息内容
        B->>B: 8. 验证Key是否匹配
    end
    
    B-->>U: 9. 返回所有匹配的消息

重点: IndexFile 特性

特性 说明

容量 单个IndexFile最多可以存储 2000W 个索引条目

哈希槽 固定 500W 个槽位,使用链表法解决哈希冲突

时间范围 每个IndexFile有时间范围,查询时需要扫描多个文件

异步构建 由后台线程从CommitLog异步构建索引

IndexFile 特性

🔗 三大存储的协作关系

完整的消息生命周期

graph TB
    subgraph Write["消息写入阶段"]
        P[Producer发送消息]
        W1[写入CommitLog]
        W2[异步构建ConsumeQueue]
        W3[异步构建IndexFile]
        
        P --> W1
        W1 --> W2
        W1 --> W3
    end
    
    subgraph Read1["普通消费流程"]
        C1[Consumer请求消息]
        R1[读取ConsumeQueue获取偏移量]
        R2[根据偏移量读取CommitLog]
        R3[返回消息给Consumer]
        
        C1 --> R1 --> R2 --> R3
    end
    
    subgraph Read2["Key查询流程"]
        C2[根据Key查询]
        R4[读取IndexFile获取偏移量]
        R5[根据偏移量读取CommitLog]
        R6[返回查询结果]
        
        C2 --> R4 --> R5 --> R6
    end
    
    Write -.->|支持| Read1
    Write -.->|支持| Read2
    
    style Write fill:#ffec99,stroke:#f59f00
    style Read1 fill:#b2f2bb,stroke:#2f9e44
    style Read2 fill:#a5d8ff,stroke:#1971c2

重点: 设计优势分析

读写分离:

写入时:只需要顺序写 CommitLog,性能极高 读取时:通过 ConsumeQueue 和 IndexFile 快速定位

空间换时间:

ConsumeQueue 和 IndexFile 占用额外空间 换来了消费和查询的高性能

异步构建:

索引异步构建不影响消息写入性能 保证了系统的高吞吐量

灵活查询:

支持顺序消费(ConsumeQueue) 支持Key查询(IndexFile) 支持时间范围查询

设计优势分析

性能优化技术

1. 内存映射文件(MMap)

RocketMQ 使用 MappedByteBuffer(内存映射文件)技术,将文件直接映射到内存地址空间,减少数据拷贝次数。

graph LR
    subgraph Traditional["传统IO"]
        T1[应用程序] -->|read/write| T2[内核缓冲区]
        T2 -->|拷贝| T3[用户空间缓冲区]
        T3 -->|拷贝| T4[磁盘]
    end
    
    subgraph MMap["MMap内存映射"]
        M1[应用程序] -->|直接访问| M2[内存映射区]
        M2 -->|零拷贝| M3[磁盘]
    end
    
    style MMap fill:#b2f2bb,stroke:#2f9e44
    style Traditional fill:#ffc9c9,stroke:#c92a2a

2. 顺序写

CommitLog 采用顺序追加写入,充分利用磁盘的顺序IO性能,可以达到接近内存的写入速度。

3. 页缓存(Page Cache)

操作系统会将频繁访问的文件内容缓存在内存中,RocketMQ 充分利用这一机制提升读取性能。

graph TB
    subgraph Cache["页缓存工作机制"]
        R[读取请求]
        PC{页缓存命中?}
        MEM[从内存返回微秒级]
        DISK[从磁盘读取毫秒级]
        UPDATE[更新页缓存]
        
        R --> PC
        PC -->|命中| MEM
        PC -->|未命中| DISK
        DISK --> UPDATE
        UPDATE --> MEM
    end
    
    style MEM fill:#b2f2bb,stroke:#2f9e44
    style DISK fill:#ffc9c9,stroke:#c92a2a

4. 文件预分配

RocketMQ 会预先分配文件空间,避免文件扩展带来的性能损耗。

5. 异步刷盘与主从同步

graph LR
    subgraph Flush["刷盘策略"]
        F1[消息写入内存]
        F2{刷盘策略}
        F3[同步刷盘可靠性高]
        F4[异步刷盘性能高]
        
        F1 --> F2
        F2 -->|立即刷盘| F3
        F2 -->|批量刷盘| F4
    end
    
    subgraph Sync["主从同步"]
        S1[Master节点]
        S2{同步策略}
        S3[同步复制可靠性高]
        S4[异步复制性能高]
        S5[Slave节点]
        
        S1 --> S2
        S2 -->|等待ACK| S3
        S2 -->|不等待ACK| S4
        S3 --> S5
        S4 --> S5
    end
    
    Flush -.->|配合| Sync
    
    style F3 fill:#ffd43b,stroke:#f59f00
    style F4 fill:#b2f2bb,stroke:#2f9e44
    style S3 fill:#ffd43b,stroke:#f59f00
    style S4 fill:#b2f2bb,stroke:#2f9e44

最佳实践建议

重点: 存储配置建议

磁盘选择:

优先使用 SSD,可显著提升性能 如使用机械硬盘,建议 RAID10 配置 单独的磁盘用于 CommitLog,提升IO性能

刷盘策略:

金融、支付场景:同步刷盘 + 同步复制 日志、监控场景:异步刷盘 + 异步复制 一般业务场景:异步刷盘 + 同步复制

文件保留:

根据业务需求设置消息保留时间 定期清理过期文件,避免磁盘占满 建议保留时间:72小时(3天)

索引优化:

合理设置消息 Key,提升检索效率 避免过长的 Key 值 定期监控 IndexFile 大小

存储配置建议

重点: 监控指标

指标类别 关键指标 正常范围

写入性能 TPS、写入延迟 TPS > 10000, 延迟 < 10ms

消费性能 消费TPS、消费延迟 无明显积压

磁盘使用 磁盘使用率、IO等待 使用率 < 80%, IO等待 < 30%

文件状态 CommitLog/ConsumeQueue 大小 定期清理,无异常增长

监控指标

总结

三大存储核心要点

mindmap
    root((RocketMQ存储架构))
        CommitLog
            顺序写入
            混合存储
            高性能写
            1GB单文件
            MMap映射
        ConsumeQueue
            消费索引
            轻量级
            20B条目
            快速定位
            Topic隔离
        IndexFile
            Key索引
            哈希表
            链表解冲突
            消息追踪
            问题排查
        协作机制
            异步构建
            读写分离
            空间换时间
            高可用性

重点: 核心设计思想

顺序写,随机读:CommitLog 顺序写保证写入性能,通过索引支持快速随机读 空间换时间:使用额外的索引文件,大幅提升查询和消费效率 异步构建:索引异步构建,不阻塞消息写入,保证系统吞吐量 分层存储:数据存储与索引分离,各司其职,提升整体性能 灵活配置:支持多种刷盘和同步策略,平衡性能与可靠性

核心设计思想

理解要点

CommitLog 是消息的实际存储,所有消息顺序写入,保证高性能。

ConsumeQueue 是消费的逻辑队列,通过轻量级索引快速定位消息,支持高效消费。

IndexFile 是消息的检索索引,支持基于 Key 的消息查询,便于问题排查和消息追踪。

三者相互配合,共同构建了 RocketMQ 高性能、高可用的消息存储系统!