RocketMQ 源码分析 消息存储(预备知识一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MakeContral/article/details/85094005

前言

看到RocketMQ的性能问题的时候,通常能看到page cache顺序IO写预读等,要想设计出一个高性能的中间件,这部分的知识是绝对要掌握的。

顺序IO读写为什么速度更快

当需要从硬盘上读取一个文件时,首先会要求磁头定位到这个文件的起始扇区。这个定位过程包括两个步骤: 磁头定位到对应的磁道; 主轴马达带动盘片转动到正确的位置。

这个过程所花费的时间被称为寻址时间。也就是说寻址时间实际上包含两部分: 磁头定位到磁道的时间为寻道时间; 等待盘片转动到正确位置的时间称为旋转等待时间。

硬盘寻址的目的是为了找到将要读取的文件的起始扇区,并开始去取数据。这就可以解释为什么硬盘上读取一个100MB大小的文件和读取1000个100KB大小的文件时间是完全不一样的现象了:

通常来说一个100MB的文件是存储在硬盘上可以连续读取的扇区上的,也就是说当硬盘需要读取这个文件时只需要进行一次寻址。 (为什么说是“通常”呢。因为前提是硬盘上至少要有一端连续空白的扇区,如果此时硬盘上碎片太多可能就找不到这样的连续空白区域了);

而读取1000个文件时,由于这些文件的起始存储位不连续,所以每次都要进行寻址操作。所花费的时间多很多。

这也就是解释了如果是顺序IO为什么更快,因为对一个文件的IO可以减少寻址时间。

从上面的描述中可以明白RocketMQ中的两个问题:

  1. 为什么要将消息都存储在一个文件中,满1G再创建新的文件。

    多文件的起始存储在磁盘中不连续,读取消息会花很多时间,通过对一个文件追加写提高IO

  2. 为什么要讲commitlog文件设计成1G大小的。

    磁盘空间预分配。创建文件时为文件分配固定大小的空间,让文件尽可能的占用连续的磁盘扇区,减少后续写入的磁盘寻道(seek)开销。

    这里还有一个原因是:RocketMQ采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次只能映射1.5~2G 的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因了

page cache

先举个例子:一位学者一上班开始撰写论文,边修改边写,如果每次修改一个词就写进磁盘中,这个IO开销太大了。所以这部分论文的数据都保存在高速缓存中。就是说这部分的数据不会马上写入到磁盘中。这部分缓存称为page cache。

这里需要提一点cache分成page cache和Buffer cache两者的区别:

在文件层面上的数据会缓存到page cache。比如你打开了一个文件,文件中的内容就会缓存到page cache中。

直接对磁盘进行操作的数据会缓存到buffer cache中,例如,文件系统的元数据都会缓存到buffer cache中。 比如ls 命令,显示的一些数据是针对文件系统,这部分的数据就会缓存在buffer cache中。

回写

还是上面的例子,因为这修改的这部分数据都是保存在了page cach,如果系统出现故障了,存放在高速缓存中的数据会随之消失。

根据LRU算法,那些经常被访问的盘块数据,可能会一直保留在高速缓存中,长期不会被写回磁盘。(注意,LRU链意味着链中任何一元素在被访问之后,总是又被挂到链尾不会被写回磁盘,只是一直未被访问的元素,才有可能移到链首,而被写回磁盘)

在Unix系统中,有些方法是通过调用SYNC。设定30s强制将缓存中的数据写回磁盘中。RocketMQ也有同步刷盘和异步刷盘的机制。在后续给解释。

预读

当一个文件被读取时,在它临近扇区所存储的文件数据也将在近期被读取。所以硬盘会预先读取后者到缓存中,以便在不久的将来,这些数据被请求读取时直接从缓存中向外部设备输出文件数据。在RocketMQ中就是将数据一些预读到缓存中,让下次的操作数据时不需要经过寻址的过程。提高cache的命中率,提高IO。

猜你喜欢

转载自blog.csdn.net/MakeContral/article/details/85094005