RocketMQ消息存储原理

消息存储

CommitLog

1、消息内容和元数据都会存在CommitLog日志文件中
2、消息内容不是定长的
3、单个文件大小默认1G
   文件名长度为20位 左边补零 剩余为起始偏移量
   
   比如第一个文件名是 00000000000000000000
   起始偏移量为0
   文件大小为1G=1073741824
   当第一个文件写满了
   第二个文件为00000000001073741824
   起始偏移量为1073741824

ConsumeQueue(消息消费队列)

主要是为了提高消息消费的性能

消费者通过消息消费队列ConsumeQueue(作为索引)
来查找消费的消息

消费队列ConsumeQueue(索引)内容

ConsumeQueue(逻辑消费队列)
作为消费消息的索引
保存了指定Topic下的队列消息在CommitLog中的
起始物理偏移量offset(8个字节)
消息大小size和(4个字节)
消息Tag的HashCode值(8个字节)

每个条目定长20个字节
单个文件有30W个条目
可以像数组一样随机访问每个条目
每个ConsumeQueue文件大约5.72M

consumequeue文件夹的组织方式

topic/queue/file三层组织结构

具体存储路径

$HOME/store/consumequeue/{topic}/{queueId}/{fileName}

IndexFile(索引文件)

通过key或时间区间来查询消息

1、存储位置
$HOME \store\index${fileName}
2、文件名
以创建时的时间戳命名的
3、文件大小
单个文件约为400M
可以存储2000W个索引

存储结构

Broker是混合型存储结构

单个broker所有队列共用一个日志文件

索引和数据分离的存储结构

生产者和消费者使用索引和数据相分离的存储结构

整体流程

1、Producer发送消息至Broker端
2、Broker端使用同步或者异步的方式对消息刷盘持久化
   保存至CommitLog中
3、Consumer拉取消息
4、服务端也支持长轮询模式
   Broker允许等待30s的时间
   只要这段时间内有新消息到达
   将直接返回给消费端

页缓存和内存映射

页缓存(PageCache)是OS对文件的缓存
用于加速对文件的读写
程序对文件进行顺序读写的速度几乎接近于内存的读写速度
主要原因就是由于OS使用PageCache机制对读写访问操作进行了性能优化
将一部分的内存用作PageCache
对于数据的写入
OS会先写入至Cache内
随后通过异步的方式由pdflush内核线程将Cache内的数据刷盘至物理磁盘上
对于数据的读取
如果一次读取文件时出现未命中PageCache的情况
OS从物理磁盘上访问读取文件的同时
会顺序对其他相邻块的数据文件进行预读取

消费队列的读性能几乎接近读内存的原因

在消息堆积的情况下也不会影响性能

  • 存储的数据较少
  • page cache机制预读取
  • 顺序读取

消息存储的日志数据文件CommitLog的读取会严重影响性能

读取消息内容会产生较多的随机访问读取

如何提升随机读性能

在块存储采用SSD的话 
选择合适的系统IO调度算法 
比如Deadline算法
随机读的性能会有所提升

文件的读写操作

传统的IO方式

传统的IO方式
用户态空间的进程要读写磁盘文件
需要经过内核空间
用户进程访问内核空间的缓存
如果没有的话 则读取磁盘文件
用户进程写入文件 先写入内核空间的socket缓存
再通过网卡写入到磁盘

经过2次DMA拷贝+2次CPU拷贝
4次上下文切换

RocketMQ mmap+write

1、页缓存是对磁盘数据的缓存
2、用户先读取页缓存 
如果没有数据则读取磁盘
根据局部性原理
将相邻磁盘块读入页缓存
3、用户将数据写入页缓存
异步线程将小的写入操作合并成大的写入
然后刷入磁盘
4、顺序写入磁盘 磁头几乎不用换道 
5、异步刷盘 容易丢失数据
a、可以同步刷盘 但性能低
b、一般采用多副本机制保证消息的可靠
6、数据追加到日志文件的尾部
老的消息无法更改

mmap利用内存映射文件来避免拷贝

用户空间可以通过映射地址加偏移量的方式直接操作内核空间的页缓存
避免了内核态再拷贝到用户态

rocketmq采用mmap+write方式实现文件读写操作

产生2次DMA拷贝+1次CPU拷贝
4次上下文切换
通过内存映射减少了一次CPU拷贝
可以减少内存使用
适合大文件的传输

对比Kafafa sendfile

sendfile

1、用户进程通过sendfile()方法向操作系统发起调用上下文从用户态转向内核态
2、DMA控制器把数据从硬盘中拷贝到读缓冲区
3、CPU将读缓冲区中数据拷贝到socket缓冲区
   DMA控制器把数据从socket缓冲区拷贝到网卡
   上下文从内核态切换回用户态
   sendfile调用返回

2次用户态和内核态的上下文切换
3次拷贝

sendfile方法IO数据对用户空间完全不可见
所以只能适用于完全不需要用户空间处理的情况
比如静态文件服务器

sendfile+DMA gather

1、用户进程通过sendfile()方法向操作系统发起调用 
  上下文从用户态转向内核态
2、DMA控制器利用scatter把数据从硬盘中拷贝到读缓冲区离散存储
3、CPU把读缓冲区中的文件描述符和数据长度发送到socket缓冲区
4、DMA控制器根据文件描述符和数据长度  
   使用scatter/gather把数据从内核缓冲区拷贝到网卡
5、sendfile()调用返回,上下文从内核态切换回用户态

DMA gather和sendfile一样数据对用户空间不可见
而且需要硬件支持
同时输入文件描述符只能是文件
但是过程中完全没有CPU拷贝过程
极大提升了性能

产生2次DMA拷贝
没有CPU拷贝
而且也只有2次上下文切换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值