一、存储介质
● 关系型数据库DB
Apache下开源的另外一款MQ—ActiveMQ (默认采用的KahaDB做消息存储)可选用JDBC的方式来做消息持久化,通过简单的xmI配置信息即可实现JDBC消息存储。由于,普通关系型数据库(如Mysql)在单表数据量达到千万级别的情况下,其IO读写性能往往会出现瓶颈。在可靠性方面,该种方案非常依赖DB,如果一旦DB出现故障,则MQ的消息就无法落盘存储会导致线上故障
● 文件系统
目前业界较为常用的几款产品(RocketMQ/Kafka/RabbitMQ)均采用的是消息刷盘至所部署虚拟机/物理机的文件系统来做持久化(刷盘-般可以分为异步刷盘和同步刷盘两种模式)。消息刷盘为消息存储提供了一种高效率、可靠性和高性能的数据持久化方式。除非部署MQ机器本身或是本地磁盘挂了,否则一般是不会出现无法持久化的故障问题。
性能对比:文件系统>关系型数据库DB
二、消息的存储和发送
1)消息存储
目前的高性能磁盘,顺序写速度可以达到600MB/s,超过了一般网卡的传输速度。
但是磁盘随机写的速度只有大概1 00KB/s,和顺序写的性能相差6000倍!
因为有如此巨大的速度差别,好的消息队列系统会比普通的消息队列系统速度快多个数量级。
RocketMQ的消息用顺序写,保证了消息存储的速度。
2)存储结构
RocketMQ消息的存储是由ConsumeQueue和CommitLog配合完成的,消息真正的物理存储文件是CommitLog,ConsumeQueue 是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。每个Topic下的每个Message Queue都有一个对应的ConsumeQueue文件。
● 消息存储架构图中主要有下面三个跟消息存储相关的文件构成。
(1) CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。单个文件大小默认1G,文件名长度为20位,左边补零,剩余为起始偏移量,比如000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件;
(2) ConsumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能
RocketMQ是基于主题topic的订阅模式,消息消费是针对主题进行
如果要遍历commitlog文件根据topic检索消息是非常低效。
Consumer即可根据ConsumeQueue来查找待消费的消息。
其中,ConsumeQueue (逻辑消费队列)作为消费消息的索引:
保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset、消息大小size、消息Tag的HashCode值。
consumequeue文件可以看成是基于topic的commitlog索引文件,故consumequeue文件夹的组织方式如下:
topic/queue/file三层组织结构
具体存储路径为:$HOME/store/consumequeue/{topic}/{queueld}/{fileName}。
consumequeue文件采取定长设计,每个条目共20个字节,分别为:
8字节的commitlog物理偏移量、4字节的消息长度、8字节tag hashcode
单个文件由30W个条目组成,可以像数组一样随机访问每一个条目
每个ConsumeQueue文件大小约5.72M;
(3) IndexFile:IndexFile (索引文件)提供了一种可以通过key或时间区间来查询消息的方法。
1. Index文件的存储位置是:$HOME/store/index/${fiTeName}
2.文件名fileName是以创建时的时间戳命名的
3.固定的单个IndexFile文件大小约为400M
4.一个IndexFile可以保存2000W个索引
5. IndexFile的底层存储设计为在文件系统中实现HashMap结构,故rocketmq的索引文件其底层实现为hash索引.