RocketMQ消息存储学习

Page Cache

page cache 是 Linux内核实现的磁盘缓存,主要用来减少对磁盘的I/O操作,把磁盘访问变成物理内存访问

无论通过标准I/O还是mmap,首先都是读入page cache,page cache 内核地址映射到page的物理地址。如果是标准I/O就拷贝到用户地址,如果是mmap,则将用户地址映射到相同的page物理地址。
不过mmap只建立了地址映射,所以当进程发起对映射空间访问时会引发缺页异常,从而引发磁盘文件到物理内存的拷贝。

以下参考链接[2]
标准I/O过程:
假设一个名为render的Linux程序,它打开文件scene.dat并一次读取512个字节,将文件内容存储到堆分配的块中。第一次读是这样的:
在这里插入图片描述
在读取了12KB以后,render的堆以及相关的页帧情况如下,上面的是page cache,下面的是用户地址空间对应的物理内存:
在这里插入图片描述
mmap
而mmap则直接将render进程的用户地址空间直接指向了page cache。
在这里插入图片描述
page cache 刷盘
page cache修改之后会变成脏页。
脏页有两种刷盘的方式:

  1. 异步刷盘,脏页太多或者存在太久都会导致page cache回写磁盘,Linux中可以通过 sysctl -a | grep dirty 查看相关的控制参数。
  2. 调用fsync、msync等。

RocketMQ消息存储

RocketMQ文件映射关系
下图来自Apache RocketMQ 中国开发者钉钉群陈厚道老师的PPT
在这里插入图片描述
RocketMQ中每个文件对应一个MappedFile,MappedFile由MappedFileQueue管理。

RocketMQ消息存储的两种方式
下图来自Apache RocketMQ 中国开发者钉钉群刘春龙老师的PPT
在这里插入图片描述
TransientStorePool主要功能是池化管理多个DirectByteBuffer对象,可以向其借与还Buffer提供给MappedFile使用。
使用DirectByteBuffer的原因可以看一下另一篇文章《Java直接内存原理》
写数据
先讲一下上图的第二种的方式:

  • appendMessage直接写到借来的writeBuffer中,调用commit方法时再通过FileChannel#write()方法写数据

第一种方式(默认方式):

  • 通过FileChannel#map获得的MappedByteBuffer直接写数据

由上面的Page Cache可知两种方式操作的是同一块物理内存(MappedByteBuffer和FileChannel没有直接关系,即使FileChannel#close(),MappedByteBuffer还是可以操作文件)。
读数据
读数据只有一种方式,直接通过MappedByteBuffer读取。
关于刷盘
调用flush会将Page Cache中的数据刷到磁盘。

另外,关于两种方式,胡宗棠老师的见解。
在这里插入图片描述
个人想法

  1. DirectByteBuffer的方式如果掉电会丢失更多的数据。
  2. mmap + pagecache的方式数据更新更及时,毕竟读数据都是从mmap 读的。

[1]《Linux内核设计与实现》
[2] https://manybutfinite.com/post/page-cache-the-affair-between-memory-and-files/
[3] http://www.quora.com/Linux-Kernel/What-is-the-major-difference-between-the-buffer-cache-and-the-page-cache

猜你喜欢

转载自blog.csdn.net/zxcc1314/article/details/88630204