Alibaba Cloud Linux 2系统的ECS实例中ext4文件系统的Buffer I/O写性能不符合预期,如何处理?

本文介绍Alibaba Cloud Linux 2系统的ECS实例中ext4文件系统的Buffer I/O写性能不符合预期问题的原因及解决方案。

问题描述

在ext4文件系统中执行常规的缓存异步I/O(Buffer I/O)写操作时,可能会观察到性能表现不符合预期的情形。存在该问题的ECS实例有以下特征:

  • 镜像:aliyun-2.1903-x64-20G-alibase-20190327.vhd及之后所有的镜像版本。

  • 内核:kernel-4.19.24-9.al7及之后所有的内核版本。

  • 通过dioread_nolocknodelalloc两个选项挂载ext4文件系统。

    说明

    关于如何查看文件系统类型及挂载选项,请参见查看文件系统类型及挂载选项

验证性能表现不符合预期的典型场景如下:

说明

关于ECS块存储的性能,请参见块存储性能

  • 场景一:使用cp命令拷贝大文件到ext4文件系统中,耗时很久,每秒拷贝速度只能达到30MB/s左右。

  • 场景二:使用类似如下不带同步标志(Sync Flag)的dd命令写文件到ext4文件系统中,耗时很久。

    dd if=/dev/zero of=/mnt/badfile bs=10M count=1000

    且在终端中通过iostat -xm 1命令观察对应磁盘的写入速度时,wMB/s列的值只有30MB/s左右。系统显示类似如下。

    avg-cpu:  %user   %nice %system %iowait  %steal   %idle
               0.00    0.00   12.77    0.00    0.00   87.23
    
    Device:         rrqm/s   wrqm/s     r/s     w/s    rMB/s    wMB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
    vda               0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
    vdb               0.00  7194.00    0.00   57.00     0.00    28.05  1008.00     0.02   17.81    0.00   17.81   0.39   2.20

问题原因

从文件系统代码层面分析,该问题的根本原因是当文件系统以dioread_nolocknodelalloc组合选项挂载时,产生了大量内核中被称为“unwritten extents”、大小为4KB的脏页(Dirty Page);由于ext4文件系统处理逻辑的缺陷,这些4KB的脏页在写回的时候并不会被合并成若干个大页进行写回,而是直接以小页的形式被处理。从Perf工具观察内核写回页面缓存的过程,发现处理过程主要发生在ext4文件系统的ext4_writepages()函数内部,在查找和映射4KB脏页的逻辑中耗费了大量的时间,从而造成文件写入性能极低。

解决方案

该问题为已知问题,暂无固化解决方案。您可以参考以下步骤临时解决此问题:

  1. 参考以下命令,重新挂载ext4文件系统,取消同时使用dioread_nolocknodelalloc的挂载选项组合。

    sudo mount -o remount,delalloc <$Device> <$Mount_point>
    说明
    • <$Device>:指挂载ext4文件系统的设备名。

    • <$Mount_point>:指ext4文件系统的挂载点。

  2. 修改/etc/fstab文件,删除ext4文件系统的nodelalloc选项(默认为delalloc),以确保系统开机自动挂载。

查看文件系统类型及挂载选项

查看目录所在磁盘的文件系统类型及挂载选项,参考步骤如下:

  1. 登录ECS实例,参考以下命令,确认目录所在的磁盘分区。

    df <$DIR> | grep -v Filesystem | awk '{ print $1 }'
    说明

    <$DIR>指写操作的目标目录。

  2. 参考以下命令,查看目标磁盘分区的文件系统类型以及挂载选项。

    mount | grep -w <$Partition> | grep ext4 | grep -w dioread_nolock | grep -w nodelalloc
    说明

    <$Partition>指上一步获取的磁盘分区名。