RocketMQ(十):数据存储模型设计与实现

  消息中间件,说是一个通信组件也没有错,因为它的本职工作是做消息的传递。然而要做到高效的消息传递,很重要的一点是数据结构,数据结构设计的好坏,一定程度上决定了该消息组件的性能以及能力上限。

 

1. 消息中间件的实现方式概述

  消息中间件实现起来自然是很难的,但我们可以从某些角度,简单了说说实现思路。

  它的最基本的两个功能接口为:接收消息的发送(produce), 消息的消费(consume). 就像一个邮递员一样,经过它与不经过它实质性的东西没有变化,它只是一个中介(其他功能效应,咱们抛却不说)。

  为了实现这两个基本的接口,我们就得实现两个最基本的能力:消息的存储和查询。存储即是接收发送过来的消息,查询则包括业务查询与系统自行查询推送。

 

我们先来看第一个点:消息的存储。

  直接基于内存的消息组件,可以做到非常高效的传递,基本上此时的消息中间件就是由几个内存队列组成,只要保证这几个队列的安全性和实时性,就可以工作得很好了。然而基于内存则必然意味着能力有限或者成本相当高,所以这样的设计适用范围得结合业务现状做下比对。

  另一个就是基于磁盘的消息组件,磁盘往往意味着更大的存储空间,或者某种程度上意味着无限的存储空间,因为毕竟所有的大数据都是存放在磁盘上的,前提是系统需要协调好各磁盘间的数据关系。然而,磁盘也意味着性能的下降,数据存放起来更麻烦。但rocketmq借助于操作系统的pagecache和mmap以及顺序写机制,在读写性能方面已经非常优化。所以,更重要的是如何设计好磁盘的数据据结构。

 

然后是第二个点:消息的查询。

  具体如何查询,则必然依赖于如何存储,与上面的原理类似,不必细说。但一般会有两种消费模型:推送消息模型和拉取消费模型。即是消息中间件主动向消费者推送消息,或者是消费者主动查询消息中间件。二者也各有优劣,推送模型一般可以体现出更强的实时性以及保持比较小的server端存储空间占用,但是也带来了非常大的复杂度,它需要处理各种消费异常、重试、负载均衡、上下线,这不是件小事。而拉取模型则会对消息中间件减轻许多工作,主要是省去了异常、重试、负载均衡类的工作,将这些工作转嫁到消费者客户端上。但与此同时,也会对消息中间件提出更多要求,即要求能够保留足够长时间的数据,以便所有合法的消费者都可以进行消费。而对于客户端,则也需要中间件提供相应的便利,以便可以实现客户端的基本诉求,比如消费组管理,上下线管理以及最基本的高效查询能力。

 

2. rocketmq存储模型设计概述

  很明显,rocketmq的初衷就是要应对大数据的消息传递,所以其必然是基于磁盘的存储。而其性能如上节所述,其利用操作系统的pagecache和mmap机制,读写性能非常好,另外他使用顺序写机制,使普通磁盘也能体现出非常高的性能。

  但是,以上几项,只是为高性能提供了必要的前提。但具体如何利用,还需要从重设计。毕竟,快不是目的,实现需求才是意义。

  rocketmq中主要有四种存储文件:commitlog 数据文件, consumequeue 消费队列文件, index 索引文件, 元数据信息文件。最后一个元数据信息文件比较简单,因其数据量小,方便操作。但针对前三个文件,都会涉及大量的数据问题,所以必然好详细设计其结构。

  从总体上来说,rocketmq都遵从定长数据结构存储,定长的最大好处就在于可以快速定位位置,这是其高性能的出发点。定长模型。

  从核心上来说,commitlog文件保存了所有原始数据,所有数据想要获取,都能从或也只能从commitlog文件中获取,由于commitlog文件保持了顺序写的特性,所以其性能非常高。而因数据只有一份,所以也就从根本上保证了数据一致性。

  而根据各业务场景,衍生出了consumequeue和index文件,即 consumequeue 文件是为了消费者能够快速获取到相应消息而设计,而index文件则为了能够快速搜索到消息而设计。从功能上说,consumequeue和index文件都是索引文件,只是索引的维度不同。consumequeue 是以topic和queueId维度进行划分的索引,而index 则是以时间和key作为划分的索引。有了这两个索引之后,就可以为各自的业务场景,提供高性能的服务了。具体其如何实现索引,我们稍后再讲!

  commitlog vs consumequeue 的存储模型如下:

RocketMQ(十):数据存储模型设计与实现

 

3. commitlog文件的存储结构

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwxgd.html