在NFS(Network File System)文件系统中通过read、copy_file_range等系统调用读取文件时,与同场景下的Alibaba Cloud Linux 2相比,可能会存在明显的性能退化情况。本文介绍在Alibaba Cloud Linux 3系统的ECS实例中,在NFS文件系统下读取文件时性能不符合预期的问题原因及解决方案。
问题描述
问题表现
在Alibaba Cloud Linux 3系统的NFS文件系统中读取文件时性能不符合预期,典型表现如下:
使用read、copy_file_range等系统调用从NFS文件系统中读取大文件时,耗时较久。
使用
dd
命令从NFS挂载点的文件中读取数据时,Alibaba Cloud Linux 3比Alibaba Cloud Linux 2用时多。例如:dd if=<nfs_mntpoint>/<testfile> of=/dev/null bs=1M
说明该示例命令表示从NFS挂载点的testfile文件中读取数据,并将其发送到/dev/null设备中。运行完毕后,
dd
会输出一些信息,包括读取的总字节数以及操作耗费的时间,用来计算读取数据的速率,可以评估NFS文件系统的性能表现。
影响范围
该问题主要在以下ECS实例中存在:
镜像:aliyun_3_x64_20G_alibase_20210415.vhd及之后的所有镜像版本。
内核:5.10.23-4.al8.x86_64及之后的所有内核版本。
文件系统:挂载NFS文件系统,读取位于挂载点目录下的文件。
问题原因
在上游Linux内核中,read_ahead_kb
表示块设备的预读窗口大小的参数。预读(read-ahead)是一个性能优化技术,它允许系统预测接下来可能被读取的数据并提前加载到内存中,这样当这部分数据被请求时,就可以直接从内存中读取,而不必等待磁盘I/O操作,从而降低延迟并提高数据读取效率。
在Linux内核5.4版本之前,NFS文件系统的预读量通常会基于挂载时设置的
rsize
参数(即每次NFS读请求的大小)。默认情况下,NFS的预读窗口read_ahead_kb
大小被设置为rsize
参数的15倍。Alibaba Cloud Linux 2的内核版本是4.19,默认的rsize
参数大小为1,024 KB,即read_ahead_kb
大小为15,360 KB。然而,在Linux内核5.4版本中引入了一个提交(index : kernel/git/torvalds/linux.git),随后
read_ahead_kb
的值不再基于rsize
参数,而是与VM_READAHEAD_PAGES
参数相关。Alibaba Cloud Linux 3的内核版本是5.10,默认的read_ahead_kb
大小为128 KB。
所以,Alibaba Cloud Linux 3相比于Alibaba Cloud Linux 2的读取文件性能有所下降。因此Alibaba Cloud Linux 3需要重新评估和调整预读窗口大小,以优化文件的读取效率。
在NFS文件系统中,较大的预读窗口可能会提高大型文件连续读取的性能,但如果窗口太大,也可能导致不必要的数据被加载到内存中,尤其是在随机读取的场景中。因此建议您根据实际的业务场景,仔细评估实际的工作负载,调整read_ahead_kb
参数值以确保找到最佳的预读窗口大小。
解决方案
您可以选择以下任意一种方式修改read_ahead_kb
参数来设置预读值。
通过echo
命令修改(单个文件系统修改)
查看NFS系统目标设备当前的预读参数。
cat /sys/class/bdi/$(mountpoint -d <nfs_mountpoint>)/read_ahead_kb
其中
<nfs_mountpoint>
需替换为实际的NFS挂载点路径,可以通过cat /proc/self/mountinfo
命令获取。适当调大NFS文件系统所对应设备的预读参数。
sudo sh -c 'echo <num> > /sys/class/bdi/<major>:<minor>/read_ahead_kb'
您需要根据实际环境替换以下参数:
<num>
:需要设置的预读窗口大小(单位KB)。<major>:<minor>
:NFS文件系统的主要和次要设备号,可以通过sudo mountpoint -d <nfs_mountpoint>
命令获取。
例如:
sudo sh -c 'echo 15360 > /sys/class/bdi/0:422/read_ahead_kb'
说明如果您挂载了多个NFS文件系统,需要重复执行命令修改每一个设备的预读参数。
通过udev机制自动修改(多个文件系统修改)
您也可以利用udev机制,添加udev规则,为所有已挂载的设备手动触发一次udev事件,使其触发udev规则检查并自动修改预读参数,也可以使后续新挂载的文件卷自动修改其预读参数。操作步骤如下:
udev(用户空间设备管理器)是Linux内核的一个子系统,负责设备节点的管理和自动化。udev机制的核心组件是udev守护进程,它运行于用户空间,并与内核通过uevent机制进行通信。
打开并编辑NFS的udev rules配置文件(位于
/etc/udev/rules.d/
目录)。如果不存在udev rules配置文件,请先自行创建。例如:sudo vim /etc/udev/rules.d/99-nfs.rules
在打开的文件中,添加udev规则,以自动修改预读参数。
该示例表示将
read_ahead_kb
的值设置为15,360 KB,您可以根据需要修改预读参数。SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="/bin/awk -v bdi=$kernel 'BEGIN{ret=1} {if ($4 == bdi) {ret=0}} END{exit ret}' /proc/fs/nfsfs/volumes", ATTR{read_ahead_kb}="15360"
保存并关闭文件。
重新加载udev规则,使新增规则生效。
sudo udevadm control --reload
为所有已经挂载的设备手动触发一次udev事件,将已挂载设备的
read_ahead_kb
值一并修改。sudo udevadm trigger -c add -s bdi
通过修改NFS的配置文件(2.3.3-57.0.1.al8.1及之后版本的多个文件系统修改)
如果您的Alibaba Cloud Linux 3的NFS文件系统版本是nfs-utils-2.3.3-57.0.1.al8.1及之后的版本(可以通过rpm -qa | grep nfs-utils
命令查询 ),可以通过修改NFS的配置文件来修改read_ahead_kb
参数(位于 /etc/nfs.conf
目录 )。
打开并编辑NFS的配置文件。
sudo vim /etc/nfs.conf
修改默认预读值,然后保存并关闭文件。
[nfsrahead] nfs=15000 nfs4=16000
请根据实际挂载的NFS文件系统版本(nfs/nfsv4)修改对应参数值。
nfs
表示NFS文件系统协议版本是3;nfs4
表示NFS文件系统协议版本是4,您可以通过mount -v | grep nfs
命令查询。对于已挂载的NFS文件系统,需要手动卸载后重新挂载使配置生效。
sudo umount <nfs_mountpoint> sudo mount -t nfs -o vers=<NFS协议版本> <NFS服务器地址> <nfs_mountpoint>