引言
对于一个优秀的消息中间件来说,持久化机制应该是最值得讨论和学习的,本文将深入了解RocketMQ的持久化机制帮助我们来理解优秀的开源框架
RocketMQ 在持久化的设计上,采取的是消息顺序写、随机读的策略,利用磁盘顺序写的速度,让磁盘的写速度不会成为系统的瓶颈。并且采用 MMPP 这种“零拷贝”技术,提高消息存盘和网络发送的速度。极力满足 RocketMQ 的高性能、高可靠要求。
先来看看RocketMQ架构图
底层存储结构
- CommitLog:RocketMQ 所有消息的实际存储文件,所有主题的消息都顺序存储在一个 CommitLog 文件中,这样可以避免随机写磁盘,提高写入性能。
- ConsumeQueue:它是消息消费的逻辑队列,每个主题下的每个队列都有一个对应的 ConsumeQueue 文件。ConsumeQueue 中存储的是指向 CommitLog 中消息的索引信息,包括消息在 CommitLog 中的偏移量、消息大小、消息 Tag 的 HashCode 等,主要用于消费者快速定位消息在 CommitLog 中的位置。
- IndexFile:为了方便根据消息 Key 快速查询消息,RocketMQ 还提供了 IndexFile 索引文件。它记录了消息 Key 与消息在 CommitLog 中偏移量的对应关系,通过 IndexFile 可以快速定位到包含特定 Key 的消息在 CommitLog 中的位置。
RocketMQ刷盘机制
同步刷盘和异步刷盘,可以通过 Broker 配置文中中的 flushDiskType 参数来设置(SYNC_FLUSH、ASYNC_FLUSH)。
- 异步刷盘方式(默认):消息写入到内存的 PAGECACHE中,就立刻给客户端返回写操作成功,当 PAGECACHE 中的消息积累到一定的量时,触发一次写操作,将 PAGECACHE 中的消息写入到磁盘中。这种方式吞吐量大,性能高,但是 PAGECACHE 中的数据可能丢失,不能保证数据绝对的安全。
- 同步刷盘方式:消息写入内存的 PAGECACHE 后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。这种方式可以保证数据绝对安全,但是吞吐量不大。
存储流程
- 消息写入:当生产者发送消息到 RocketMQ 时,Broker 接收到消息后,首先会将消息写入到 CommitLog 文件中。消息会按照顺序追加的方式写入,这样可以充分利用磁盘的顺序写入特性,提高写入效率。
- 索引构建:在消息写入 CommitLog 的同时,RocketMQ 会根据消息的主题、队列等信息,构建 ConsumeQueue 和 IndexFile 索引。ConsumeQueue 记录了消息在 CommitLog 中的偏移量等信息,方便消费者快速定位消息。IndexFile 则为根据消息 Key 查询消息提供了索引支持。
- 消息刷盘:根据配置的刷盘策略,消息会在合适的时机进行刷盘操作。如果是同步刷盘,会等待消息真正写入磁盘后才返回;如果是异步刷盘,消息会先在内存中缓存,然后由专门的刷盘线程异步将数据刷写到磁盘上。
- 消息存储优化:RocketMQ 采用了多种优化措施来提高消息存储的性能和效率。例如,使用内存映射文件(MappedByteBuffer)来实现文件的读写,这样可以减少系统调用的开销,提高读写速度。同时,还采用了 PageCache 技术,利用操作系统的内存缓存机制,将经常访问的文件数据缓存在内存中,进一步提高读写性能。
高可用保障
- 主从复制:RocketMQ 采用主从架构来实现消息存储的高可用。主节点负责处理消息的写入和读取请求,从节点则实时从主节点复制数据。当主节点出现故障时,从节点可以切换为主节点,继续提供服务,保证消息的可靠性和可用性。
- 数据恢复:在系统故障或重启后,RocketMQ 可以通过 CommitLog 和 ConsumeQueue 等文件进行数据恢复。在启动时,Broker 会检查这些文件的完整性,并根据需要进行数据修复和重建索引等操作,确保消息数据的一致性和完整性。