5. 优化 NFS 性能

仔细分析您的环境,无论是从客户端还是服务器端来看,都是获得最佳 NFS 性能的必要第一步。第一部分将讨论通常对客户端重要的问题。稍后(第 5.3 节 及之后),将讨论服务器端问题。在这两种情况下,这些问题都不会完全局限于一方或另一方,但是为了更清楚地了解因果关系,将两者分开是有用的。

除了常规的网络配置——适当的网络容量、更快的 NIC、全双工设置以减少冲突、交换机和集线器之间网络速度的一致性等——最重要的客户端优化设置之一是 NFS 数据传输缓冲区大小,由 mount 命令选项指定rsizewsize.

5.1. 设置块大小以优化传输速度

mount 命令选项rsizewsize指定客户端和服务器之间来回传递的数据块的大小。如果没有rsizewsize指定选项,则默认值因我们使用的 NFS 版本而异。最常见的默认值是 4K(4096 字节),尽管对于 2.2 内核中基于 TCP 的挂载,以及从 2.4 内核开始的所有挂载,服务器指定默认块大小。

NFS V2 协议的理论限制是 8K。对于 V3 协议,限制是服务器特定的。在 Linux 服务器上,最大块大小由内核常量的值定义NFSSVC_MAXBLKSIZE,可以在 Linux 内核源文件中找到./include/linux/nfsd/const.h。截至 2.4.17,内核的当前最大块大小为 8K(8192 字节),但截至撰写本文时,在 2.4 系列中实现通过 TCP/IP 传输 NFS 的补丁集使用 32K 的值(在补丁中定义为 32*1024)作为最大块大小。

所有 2.4 客户端当前都支持高达 32K 的块传输大小,允许从其他服务器(如 Solaris)跨 NFS 挂载进行标准的 32K 块传输,而无需客户端修改。

默认值可能太大或太小,具体取决于硬件和内核的特定组合。一方面,某些 Linux 内核和网卡的组合(主要在较旧的机器上)无法处理如此大的块。另一方面,如果它们可以处理更大的块,则更大的尺寸可能会更快。

您将需要进行实验并找到一个rsizewsize可行的并且尽可能快的方案。如果您的网络环境不繁忙,您可以使用一些简单的命令来测试您的选项的速度。请注意,除非您求助于使用更复杂的基准测试,例如 BonnieBonnie++IOzone,否则您的结果可能会有很大差异。

以下命令中的第一个命令从特殊文件传输 16384 个 16k 的块/dev/zero(如果您读取它,它会非常快速地吐出零)到挂载的分区。我们将计时以查看需要多长时间。因此,从客户端机器,键入

    # time dd if=/dev/zero of=/mnt/home/testfile bs=16k count=16384

这将创建一个 256Mb 的零字节文件。通常,您应该创建一个至少是服务器上系统 RAM 两倍大的文件,但请确保您有足够的磁盘空间!然后将文件读回客户端机器上的大黑洞(/dev/null),方法是键入以下内容

    # time dd if=/mnt/home/testfile of=/dev/null bs=16k

重复几次并平均所需时间。务必每次都卸载并重新挂载文件系统(客户端和服务器本地也要卸载和重新挂载,如果您很热衷的话),这应该会清除任何缓存。

然后卸载,并使用更大和更小的块大小再次挂载。它们应该是 1024 的倍数,并且不大于系统允许的最大块大小。请注意,NFS 版本 2 限制为最大 8K,无论NFSSVC_MAXBLKSIZE定义的块大小最大为多少;版本 3 将支持高达 64K,如果允许的话。块大小应该是 2 的幂,因为大多数会约束它的参数(例如文件系统块大小和网络数据包大小)也是 2 的幂。但是,一些用户报告说,块大小不是 2 的幂,但仍然是文件系统块大小和网络数据包大小的倍数时,效果更好。

使用更大的大小直接挂载后,cd 进入挂载的文件系统,并执行诸如 ls 之类的操作,探索文件系统,以确保一切都应如此。如果rsize/wsize太大,则症状非常奇怪且不是 100% 明显的。一个典型的症状是在执行 ls 时文件列表不完整,并且没有错误消息,或者读取文件神秘地失败,也没有错误消息。在确定给定的rsize/ wsize有效后,您可以再次进行速度测试。不同的服务器平台可能具有不同的最佳大小。

记住编辑/etc/fstab以反映您发现最理想的rsize/wsize

如果您的结果看起来不一致或可疑,您可能需要在更改rsizewsize值的同时更广泛地分析您的网络。在这种情况下,以下是一些可能有用的基准测试的指针

最简单且覆盖范围最广的基准测试,包括广泛的文件大小和 IO 类型——读取、写入、重读和重写、随机访问等——似乎是 IOzone。IOzone 的推荐调用(您必须具有 root 权限)包括卸载和重新挂载被测目录,以便清除测试之间的缓存,并将文件关闭时间包括在测量中。假设您已经从服务器/tmp导出到所有人foo,并且您已在本地目录中安装了 IOzone,则这应该可以工作

    # echo "foo:/tmp /mnt/foo nfs rw,hard,intr,rsize=8192,wsize=8192 0 0"
    >> /etc/fstab
    # mkdir /mnt/foo
    # mount /mnt/foo
    # ./iozone -a -R -c -U /mnt/foo -f /mnt/foo/testfile > logfile

基准测试最多应花费 2-3 小时,但当然您需要为每个感兴趣的 rsize 和 wsize 值运行它。网站提供了参数的完整文档,但上面使用的特定选项是

5.2. 数据包大小和网络驱动程序

虽然许多 Linux 网卡驱动程序都很出色,但有些却相当糟糕,包括一些相当标准的网卡的驱动程序。值得直接使用您的网卡进行实验,以找出它如何才能最好地处理流量。

尝试使用 ping 命令的-f-s选项(有关更多详细信息,请参阅 ping(8))在两台机器之间来回 ping 大数据包,看看是否丢弃了很多数据包,或者回复是否花费了很长时间。如果是这样,您的网卡性能可能存在问题。

为了更广泛地分析特别是 NFS 的行为,请使用 nfsstat 命令来查看 nfs 事务、客户端和服务器统计信息、网络统计信息等等。"-o net"选项将向您显示丢弃的数据包数量与事务总数的关系。在 UDP 事务中,最重要的统计数据是由于丢弃的数据包、套接字缓冲区溢出、一般服务器拥塞、超时等导致的重传次数。这将对 NFS 性能产生极其重要的影响,应仔细监控。请注意,nfsstat 尚未实现-z选项,该选项将清零所有计数器,因此您必须在运行基准测试之前查看当前的 nfsstat 计数器值。

要纠正网络问题,您可能希望重新配置网卡使用的数据包大小。通常,网络中的其他地方(例如路由器)存在约束,导致两台机器之间的最大数据包大小小于机器上网卡实际能够达到的最大数据包大小。TCP 应该自动发现网络的适当数据包大小,但 UDP 将简单地保持默认值。因此,如果您使用 UDP 上的 NFS,则确定适当的数据包大小尤为重要。

您可以使用 tracepath 命令测试网络数据包大小:从客户端机器,只需键入tracepath 服务器 2049路径 MTU 应在底部报告。然后,您可以使用 ifconfigMTU选项将网卡上的 MTU 设置为等于路径 MTU,并查看是否丢弃的数据包更少。有关如何重置 MTU 的详细信息,请参阅 ifconfig 手册页。

此外,netstat -s 将提供针对所有受支持协议的流量收集的统计信息。您也可以查看/proc/net/snmp以获取有关当前网络行为的信息;有关更多详细信息,请参阅下一节。

5.3. 分段数据包溢出

使用大于网络 MTU 的rsizewsize(在许多网络中通常设置为 1500)将在使用 UDP 上的 NFS 时导致 IP 数据包分段。IP 数据包分段和重组在网络连接的两端都需要大量的 CPU 资源。此外,数据包分段还会使您的网络流量更容易受到不可靠性的影响,因为如果由于任何原因丢弃了 UDP 数据包片段,则必须重新传输完整的 RPC 请求。RPC 重传的任何增加以及超时可能性增加,都是 UDP 上 NFS 性能的最大障碍。

数据包可能会因多种原因而被丢弃。如果您的网络拓扑结构复杂,则片段路由可能不同,并且可能并非全部到达服务器以进行重组。NFS 服务器容量也可能是一个问题,因为内核对它可以缓冲的片段数量有限制,然后它开始丢弃数据包。对于支持/proc文件系统的内核,您可以监视文件/proc/sys/net/ipv4/ipfrag_high_thresh/proc/sys/net/ipv4/ipfrag_low_thresh。一旦未处理的分段数据包的数量达到ipfrag_high_thresh(以字节为单位)指定的数量,内核将简单地开始丢弃分段的数据包,直到不完整数据包的数量达到ipfrag_low_thresh

要监视的另一个计数器是IP: ReasmFails在文件/proc/net/snmp中;这是分段重组失败的次数。如果在大量文件活动期间它上升太快,您可能存在问题。

5.4. TCP 上的 NFS

一个新功能,可用于 2.4 和 2.5 内核,但在撰写本文时尚未集成到主流内核中,是 TCP 上的 NFS。与 UDP 相比,使用 TCP 具有明显的优势和明显的劣势。优势在于,在丢包网络上,它的工作效果远优于 UDP。当使用 TCP 时,可以重新传输单个丢弃的数据包,而无需重新传输整个 RPC 请求,从而在丢包网络上获得更好的性能。此外,由于网络级别的底层流量控制,TCP 将比 UDP 更好地处理网络速度差异。

使用 TCP 的缺点是它不像 UDP 那样是无状态协议。如果您的服务器在数据包传输过程中崩溃,客户端将挂起,并且任何共享都需要卸载并重新挂载。

TCP 协议引起的开销将在理想的网络条件下导致比 UDP 稍慢的性能,但是成本并不严重,并且通常在没有仔细测量的情况下是无法察觉的。如果您正在使用端到端的千兆以太网,您可能还需要研究巨型帧的使用,因为高速网络可能允许更大的帧大小,而不会遇到碰撞率增加的情况,特别是如果您已将网络设置为全双工。

5.5. 超时和重传值

两个挂载命令选项,timeoretrans,控制 UDP 请求在由于丢弃的数据包、网络拥塞等而遇到客户端超时时的行为。-o timeo选项允许指定客户端等待的时间长度(以十分之一秒为单位),直到它确定它不会收到来自服务器的回复,并且必须尝试再次发送请求。默认值为十分之七秒。-o retrans选项允许指定在客户端放弃并显示服务器未响应消息之前允许的超时次数。默认值为 3 次尝试。一旦客户端显示此消息,它将继续尝试发送请求,但在发生另一次超时时,仅在显示错误消息之前尝试一次。当客户端重新建立连接时,它将回退到使用正确的retrans值,并将显示服务器正常消息。

如果您已经遇到过多的重传(请参阅 nfsstat 命令的输出),或者想要在不遇到超时和重传的情况下增加块传输大小,您可能需要调整这些值。具体调整将取决于您的环境,在大多数情况下,当前的默认值是合适的。

5.6. NFSD 服务器守护程序的实例数

大多数启动脚本(Linux 和其他脚本)都启动 8 个 nfsd 实例。在 NFS 的早期,Sun 决定了这个数字作为经验法则,其他人也都复制了。没有很好的方法来衡量多少个实例是最佳的,但是流量更大的服务器可能需要更多。您应该至少为每个处理器使用一个守护程序,但每个处理器四个到八个守护程序可能是一个更好的经验法则。如果您使用的是 2.4 或更高版本的内核,并且想要查看每个 nfsd 线程的使用情况,您可以查看文件/proc/net/rpc/nfsd。该文件中的th行的最后十个数字指示线程使用率达到最大允许百分比的时间(以秒为单位)。如果您的前三个十分位数中有大量数字,您可能希望增加 nfsd 实例的数量。这在启动 nfsd 时完成,使用实例数作为命令行选项,并在 NFS 启动脚本中指定(/etc/rc.d/init.d/nfs在 Red Hat 上)为RPCNFSDCOUNT。有关更多信息,请参阅 nfsd(8) 手册页。

5.7. 输入队列的内存限制

在 2.2 和 2.4 内核上,套接字输入队列(请求在当前正在处理时所处的位置)的默认大小限制 (rmem_default) 为 64k。此队列对于具有大量读取负载的客户端以及具有大量写入负载的服务器非常重要。例如,如果您在服务器上运行 8 个 nfsd 实例,则每个实例在处理写入请求时只有 8k 的空间来存储写入请求。此外,套接字输出队列(对于具有大量写入负载的客户端和具有大量读取负载的服务器也很重要)也具有较小的默认大小 (wmem_default).

NFS 基准测试 SPECsfs 的几个已发布运行结果指定了读写值集[rw]mem_default[rw]mem_max的更高值用法。您可以考虑将这些值增加到至少 256k。读写限制在 proc 文件系统中设置,例如使用文件/proc/sys/net/core/rmem_default/proc/sys/net/core/rmem_maxrmem_default值可以通过三个步骤增加;以下方法有点 hack,但应该有效且不会引起任何问题

最后一步可能是必要的,因为据报告,如果这些值长时间保持更改状态,机器会崩溃。

5.8. 关闭 NIC 和集线器的自动协商

如果网卡与集线器和交换机自动协商不良,并且端口以不同的速度或不同的双工配置运行,则由于过多的冲突、丢弃的数据包等,性能将受到严重影响。如果您在 nfsstat 输出中看到过多的丢弃数据包,或者总体网络性能不佳,请尝试调整网络速度和双工设置。如果可能,请专注于建立 100BaseT 全双工子网;全双工中虚拟消除冲突将消除 UDP 上 NFS 最严重的性能抑制因素。关闭网卡上的自动协商时要小心:网卡连接的集线器或交换机随后将求助于其他机制(例如并行检测)来确定双工设置,并且某些网卡默认为半双工,因为它更有可能受到旧集线器的支持。如果驱动程序支持,最好的解决方案是强制网卡协商 100BaseT 全双工。

5.9. NFS 中的同步与异步行为

NFS 版本 2 和版本 3 协议的默认导出行为,由版本 1.11 之前的 nfs-utils 中的 exportfs 使用(后者在 CVS 树中,但在 2002 年 1 月时尚未在软件包中发布)是“异步”。此默认设置允许服务器在处理完请求并将其移交给本地文件系统后立即回复客户端请求,而无需等待数据写入稳定存储。这由服务器导出列表中表示的async选项指示。它以服务器在仍然在其缓存中保存未写入数据和/或元数据时重新启动时可能的数据损坏为代价,产生更好的性能。此可能的数据损坏在发生时无法检测到,因为async选项指示服务器向客户端撒谎,告诉客户端所有数据确实已写入稳定存储,无论使用何种协议。

为了符合“同步”行为,这被用作大多数支持 NFS 的专有系统(Solaris、HP-UX、RS/6000 等)的默认设置,现在用作最新版本 exportfs 中的默认设置,Linux 服务器的文件系统必须使用sync选项导出。请注意,指定同步导出将导致在服务器的导出列表中看不到任何选项

如果您的内核是使用/proc文件系统编译的,则文件/proc/fs/nfs/exports也将显示完整的导出选项列表。

当指定同步行为时,服务器将不会完成(即,回复客户端)NFS 版本 2 协议请求,直到本地文件系统将所有数据/元数据写入磁盘。服务器完成同步 NFS 版本 3 请求而不会有此延迟,并将返回数据的状态,以便告知客户端应在其缓存中维护哪些数据,以及哪些数据可以安全丢弃。有 3 个可能的状态值,定义为枚举类型,nfs3_stable_how,在include/linux/nfs.h中。这些值以及由于这些结果而采取的后续操作如下

除了上述同步行为的定义之外,客户端还可以通过使用O_SYNC选项打开所有文件,来明确坚持完全同步行为,而与协议无关。在这种情况下,所有对客户端请求的回复都将等待,直到数据到达服务器的磁盘,而与使用的协议无关(这意味着,在 NFS 版本 3 中,所有请求都将是NFS_FILE_SYNC请求,并且将要求服务器返回此状态)。在这种情况下,NFS 版本 2 和 NFS 版本 3 的性能将几乎相同。

但是,如果使用旧的默认async行为,则O_SYNC选项在任何版本的 NFS 中都完全无效,因为服务器将在不等待写入完成的情况下回复客户端。在这种情况下,版本之间的性能差异也将消失。

最后,请注意,对于 NFS 版本 3 协议请求,来自 NFS 客户端的文件关闭时间或 fsync() 时间的后续提交请求将强制服务器将任何先前未写入的数据/元数据写入磁盘,并且服务器在完成此操作之前不会回复客户端,只要遵循sync行为即可。如果使用async,则提交本质上是空操作,因为服务器再次向客户端撒谎,告诉客户端数据已发送到稳定存储。这再次使客户端和服务器容易受到数据损坏的影响,因为客户端上缓存的数据可能会被丢弃,因为它相信服务器现在已将数据保存在稳定存储中。

5.10. 增强服务器性能的非 NFS 相关方法

一般来说,服务器性能和服务器磁盘访问速度将对 NFS 性能产生重要影响。提供有关设置运行良好的文件服务器的一般指南超出了本文档的范围,但以下一些提示可能值得一提