Mysql、RocketMq 底层存储数据结构对比

背景

作为中间件,消息存储的方式会分为两种消息存储在内存里,或者持久化在磁盘里。从可靠性上说,如果中间件重启,内存上的数据就不保存,但从效率上来说,持久化磁盘则多了一重磁盘IO。

Mysql 和 RocketMq这两种中间件,都选择将数据持久化在磁盘,作为一种可靠性上的考量。但两者从磁盘文件读取到内存储存的方式却因为使用场景的不同,选择了不同的数据结构。

Mysql innodb引擎选择将数据保存为B+树上的一个一个节点。

!](https://img-blog.csdnimg.cn/3af8b695a97341938952a0281e16e765.png)

RocketMq 则选择将数据像自己名字里带的Queue一样,以队列的形式来保存。
在这里插入图片描述

RocketMq 存储细节

两种中间件使用不同的数据结构原因是两种中间件使用的场景不一样。B+树性能稳定,通过对树节点做分裂,可以将查询的性能稳定到对数级别。队列(queue)先进先出,顺序读写。

RocketMq在业务场景上来说,并不需要像Mysql一样,做到普遍性的随机性读写,我们一般使用RocketMq作为消息中间件,使用拉模型获取某一个topic下的新消息,这样的规则有一定的顺序读写。

存储结构

RocketMq的存储结构由三个文件组成:CommitLog、ConsumeQueue、IndexFile。

CommitLog中作为消息存储的主体,所有消息的数据都保存在这个文件中,每一个CommitLog通过数据位置离初始位置的距离作为文件名。比如说,第一个CommitLog的文件名为00000000000000000000,第一个CommitLog的文件大小为1G,转换为byte为 1024 * 1024 * 1024 = 1073741824,那么下一个CommitLog则为00000000001073741824。

ConsumeQueue作为客户端直接面向的一个文件,在CommitLog上做了一层抽象,将我们使用到的topic抽象了出来。这个文件的组成形式为 topic / queue / file,这样分层的目的在于:因为各个topic的信息都保存在CommitLog中,而每个服务只关心自己的topic,不关心与自己无关的topic,那再找寻的时候,如果直接在CommitLog里顺序查找,效率就很差。通过将自己关心topic的每一个消息在CommitLog文件距离位置保存在ConsumeQueue中,就可以避免读取到其他topic的信息,只需要去顺序读取这个距离位置数据,便可以有效地先查到对应的ComitLog,再读取到文件中具体的消息。

在这里插入图片描述

IndexFile,通过它可以使用MessageId 或者 时间来查询对应的消息。这类文件是通过创建时的时间戳进行命名的,其底层存储的设计在文件系统中实现HashMap的结构。

可以看出,这三类文件的命名都是具有实际意义的,读取RocketMq中所存储的消息,都是和文件名有关的。在Mysql中的数据文件则相对独立。

页缓存与内存映射

对CommitLog 和 ConsumeQueue文件的读取,实际上对操作系统来说是两种不同的读写,ConsumeQueue形成了队列的读取,所以是顺序读取,而CommitLog则是通过ConsumeQueue中保存的数据距离位置的随机读取。

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

在RocketMQ中,ConsumeQueue逻辑消费队列存储的数据较少,并且是顺序读取,在page cache机制的预读取作用下,Consume Queue文件的读性能几乎接近读内存,即使在有消息堆积情况下也不会影响性能。而对于CommitLog消息存储的日志数据文件来说,读取消息内容时候会产生较多的随机访问读取,严重影响性能。如果选择合适的系统IO调度算法,比如设置调度算法为“Deadline”(此时块存储采用SSD的话),随机读的性能也会有所提升。

另外,RocketMQ主要通过MappedByteBuffer对文件进行读写操作。其中,利用了NIO中的FileChannel模型将磁盘上的物理文件直接映射到用户态的内存地址中(这种Mmap的方式减少了传统IO将磁盘文件数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区之间来回进行拷贝的性能开销),将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率(正因为需要使用内存映射机制,故RocketMQ的文件存储都使用定长结构来存储,方便一次将整个文件映射至内存)。

总结

说到底,RocketMq使用队列作为逻辑文件的数据结构,大部分原因是使用RocketMq的场景是顺序写、顺序读,一个消息接着一个消息读,不会出现通过MessageId随机读取消息(不考虑控制台)。而Mysql中,则是随机读写的场景更多,如果Mysql使用队列进行读写,那么算法复杂性则会高得多。可以通过mysql 中 哈希索引对比想一想就知道,mysql innodb是一种更加普遍、适合更多场景的中间件。

参考

RocketMq 设计
RocketMq 存储文件长什么样

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值