消息存储
分布式队列因为有高可靠性的要求,所以数据要进行持久化存储
- 消息生成者发送消息
- MQ收到消息,将消息进行持久化,在存储中新增一条记录
- 返回ACK给生产者
- MQ push 消息给对应的消费者,然后等待消费者返回ACK
- 如果消息消费者在指定时间内成功返回ack,那么MQ认为i消息消费成功,在存储中删除消息,即执行第6步;如果MQ在指定时间内没有收到ACK,则认为消息消费失败,会尝试重新push消息,重复执行4.5.6步骤
- MQ删除消息
存储介质
- 关系型数据库DB
- 文件系统
性能对比
文件系统>关系型数据库DB
RMQ消息存储和发送
- 消息存储
磁盘如果使用得当,磁盘的速度完全可以匹配上网络的数据传输速度。目前的高性能磁盘,顺序写速度可以达到6000MB/s,超过了一般网卡的传输速度。但是磁盘随机写的速度只有大概100kB/s,和顺序写的性能相差6000倍!因为有如此巨大的速度差别,好的消息队列系统会比普通的消息队列速度快多个数量级。RocketMQ的消息用顺序写,保证了消息存储的速度。 - 消息发送
Linux操作系统分为【用户态】和【内核态】,文件操作,网络操作需要设计这两种形态的转换,免不了进行数据复制,一台服务器把本机磁盘文件的内容发送到客户端一般分为两个步骤。
- read: 读取本的内容
- write: 将读取的内容通过网络发送出去
这两个看似简单的操作,实际进行了4次数据复制
通过使用mmap的方式,可以省区向用户态的内存复制,提高速度,这种机制在Java中是通过MappedByteBuffer实现的RocketMQ充分利用了上述特性,也就是所谓的“零拷贝”技术,提高消息存盘和网络发送的速度。
- 这里需要注意的是,采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次智能映射1.5G~2G的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个
消息存储结构
RocketMQ消息的存储是由ConsumeQueue和CommitLog配合完成的,消息真正的物理存储文件是CommitLog,ConsumeQueue是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。每个Topic下的每个MessageQueue都有一个对应的Consu,eQueue文件。
- CommitLog:催你出消息的元数据
- ConsumerQueue:存储消息在CommitLog的索引
- IndexFile:为了消息查询提供了一种通过key或事件区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费的主流程。
刷盘机制
RocketMQ的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息量超出内存的限制。RocketMQ为了提高性能,会尽可能地保证磁盘地顺序写。消息在通过Producer写入RocketMQ地时候,有两种写磁盘地方法,分布式同步刷盘和异步刷盘
- 同步刷盘
在返回写成功状态时,消息已经倍写入磁盘,具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。 - 异步刷盘
在返回写成功状态时,消息可能只是被写入内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息累计到一定程度时,统一触发写磁盘动作,快速写入。 - 配置
同步刷盘机制还是异步刷盘,都是通过Brocker配置文件里的flushDiskType参数设置的,这个参数被配置成SYNC_FLUSH,ASYNC_FLUSH中的一个。