存储:如何提升存储模块的性能和可靠性

237 阅读3分钟

提升写入操作的性能

写入优化的主要思路之一是:将数据写入到速度更快的内存中,等积攒了一批数据,再批量刷到硬盘中。

image.png

基本所有的消息队列在写入时用的都是这个方案,比如 Kafka、RocketMQ、Pulsar 就是先写入缓存,然后依赖操作系统的策略刷新数据到硬盘。

消息队列一般会同时提供:是否同步刷盘、刷盘的时间周期、刷盘的空间比例三个配置项,让业务根据需要调整自己的刷新策略。从性能的角度看,异步刷新肯定是性能最高的,同步刷新是可靠性最高的。

随机写和顺序写

单文件顺序写入硬盘很简单,硬盘控制器只需在连续的存储区域写入数据,对硬盘来讲,数据就是顺序写入的。

多文件顺序写入硬盘,系统中有很多文件同时写入,这个时候从硬盘的视角看,你会发现操作系统同时对多个不同的存储区域进行操作,硬盘控制器需要同时控制多个数据的写入,所以从硬盘的角度是随机写的。

提升写入操作的可靠性

为了提高数据可靠性,在消息队列的存储模块中,一般会通过三种处理手段:同步刷盘、WAL 预写日志、多副本备份,进一步提升数据的可靠性。

在实际落地中,我们可以采取 WAL 日志盘和实际数据盘分离的策略,提升 WAL 日志的写入速度。具体就是让 WAL 数据盘是高性能、低容量的数据盘,数据盘是性能较低、容量较大的数据盘,如果出现数据异常,就通过 WAL 日志进行数据恢复。这样,给 WAL 日志选择合适的设备,再加上并行读写等代码优化手段,性能损失就可控了,甚至可以忽略。

提升读取操作的性能

提高读取的性能主要有读热数据、顺序读、批量读、零拷贝四个思路。

image.png

零拷贝指的是数据在内核空间和用户空间之间的拷贝次数,即图中的第 2 步和第 3 步。如果只有 1 和 4 两步,没有执行 2 和 3 的话,那么内核空间和用户空间之间的拷贝次数就是零,“零拷贝”的零指的是这个次数“零”,因此是零拷贝。

image.png

  • 将数据复制链路缩短成了:硬盘 -> ReadBuffer -> 网卡设备,复制次数从四次减为两次。
  • 用户空间和内核空间之间的数据复制需要进行上下文切换,优化完复制链路后,数据只在内核空间复制传输,就可以减少两次上下文切换。
  • 通过 DMA 来搬运数据,使数据复制不需要通过 CPU,释放 CPU。

零拷贝主要用于在消费的时候提升性能,具体有两种实现方式:mmap+write 和 sendfile。

mmap 是一种内存映射文件的方法,把文件或者其他对象映射到进程的地址空间,修改内存文件也会同步修改,这样就减少了一次数据拷贝。所以,我们不需要把数据拷贝到用户空间,修改后再回写到内核空间。

image.png

sendfile() ,它可以将数据从一个文件描述符传输到另一个文件描述符。之前图中的红色线路就是通过 senfile 系统调用和 DMA 技术,将四次的数据复制次数变为了两次,提高了性能。

通过硬件和系统优化提升性能

主要可以通过提升硬件配置(如内存或硬盘)、配置多盘读写、配置硬盘阵列三个手段来提高集群的性能。


此文章为11月Day6学习笔记,内容来源于极客时间《深入拆解消息队列 47 讲》