如果我有 100 台服务器执行一个命令,并且每台服务器都将其输出重定向到 NFS 共享上的同一文件,那么生成的文件会整齐地交错还是会损坏?
例如,如果在 100 台服务器上执行此命令:
find / type -f >> /some_server/share/file_list.txt
会腐败吗?
附注该命令只是一个示例,我实际上并没有列出文件
答案1
简而言之:是的,来自多个 NFS 客户端的同时写入将会被损坏。
本地同时追加可以很好地交错,因为操作系统知道文件是以追加模式打开的,并且在每次写入调用之前自动查找文件的当前结尾,而不管同时可能扩展该文件的其他进程如何。
但在 NFS 上,就没有这样的运气了。这Linux NFS 常见问题解答明确指出:
A9。为什么在多个客户端上使用 O_APPEND 打开文件会导致文件损坏?
答:NFS 协议不支持原子追加写入,因此追加写入在任何平台的 NFS 上都不是原子的。
大多数 NFS 客户端(包括高于 2.4.20 的内核中的 Linux NFS 客户端)都支持“关闭到打开”缓存一致性,A8。什么是接近开放的缓存一致性?
答:在不同的 NFS 客户端之间实现完美的缓存一致性的成本非常高,因此 NFS 采用较弱的方式来满足大多数日常类型的文件共享的要求。 [...]当应用程序关闭文件时,NFS 客户端会将所有挂起的更改写回文件,以便下一个打开者可以查看更改。这也使 NFS 客户端有机会通过 close() 的返回代码向应用程序报告任何服务器写入错误。这种行为称为接近打开缓存一致性。
国家森林服务局写操作仅包含要写入的位置和要写入的数据。没有提供集中协调文件结尾位置的规定,而这是确保客户端不会相互覆盖所需要的。
(该页面看起来确实有点旧,因为它主要讨论 Linux 内核版本 2.6,没有提及 3.x。然而,提到了与内核支持相关的 NFS 版本 4,因此可以假设答案也适用于 v4 .)
在最近的 Linux 和 NFS v3 共享上进行了一些测试:
for ((i=0 ; i<9999 ; i++)) ; do printf "$id %06d\n" $i >> testfile ; done
同时在两个客户端上的shell 循环 ( ) 中写出数字会导致输出严重损坏。一部分:
barbar 001031
foo 000010
32
foo 000011
33
foo 000012
在这里,一台机器上的第一个循环用 写入了行barbar
,而另一台机器上的另一个循环写入了这些foo
行。应该说的行barbar 001032
是从与行相同的位置开始写入的foo 000010
,并且只有较长行的最后数字是可见的。 (请注意,在这种情况下,由于重定向位于循环内,因此实际上每个文件都会打开和关闭printf
。但它仅有助于查找文件打开时的文件末尾。)
如果文件始终保持打开状态,则可能会覆盖更大的块,因为仅承诺在文件关闭时客户端系统将更改写入服务器。即使在打开时截断文件也不会发生太大变化,因为截断只会清除文件,但不会阻止其他客户端在关闭文件时进一步写入。