我想知道为什么 NFS v4 比 NFS v3 快得多,以及 v3 上是否有任何可以调整的参数。
我挂载了一个文件系统
sudo mount -o 'rw,bg,hard,nointr,rsize=1048576,wsize=1048576,vers=4' toto:/test /test
然后运行
dd if=/test/file of=/dev/null bs=1024k
我会读200-400MB/秒 但当我将版本更改为vers=3
,重新安装并重新运行 dd 时,我只得到90MB/秒。我正在读取的文件是 NFS 服务器上的内存文件。连接的两端都是 Solaris 并且具有 10GbE NIC。我通过在所有测试之间重新安装来避免任何客户端缓存。我过去常常dtrace
在服务器上查看以测量通过 NFS 提供数据的速度。对于 v3 和 v4,我都更改了:
nfs4_bsize
nfs3_bsize
从默认的 32K 到 1M(在 v4 上,我使用 32K 最大速度达到 150MB/s)我尝试过调整
- nfs3_max_threads
- 最大连接数
- nfs3_async_clusters
以提高 v3 的性能,但没有成功。
在 v3 上,如果我运行四个并行dd
,吞吐量会从 90MB/s 下降到 70-80MBs,这让我相信问题出在某些共享资源上,如果是这样,那么我想知道它是什么,以及我是否可以增加该资源。
dtrace 代码获取窗口大小:
#!/usr/sbin/dtrace -s
#pragma D option quiet
#pragma D option defaultargs
inline string ADDR=$$1;
dtrace:::BEGIN
{
TITLE = 10;
title = 0;
printf("starting up ...\n");
self->start = 0;
}
tcp:::send, tcp:::receive
/ self->start == 0 /
{
walltime[args[1]->cs_cid]= timestamp;
self->start = 1;
}
tcp:::send, tcp:::receive
/ title == 0 &&
( ADDR == NULL || args[3]->tcps_raddr == ADDR ) /
{
printf("%4s %15s %6s %6s %6s %8s %8s %8s %8s %8s %8s %8s %8s %8s %8s\n",
"cid",
"ip",
"usend" ,
"urecd" ,
"delta" ,
"send" ,
"recd" ,
"ssz" ,
"sscal" ,
"rsz",
"rscal",
"congw",
"conthr",
"flags",
"retran"
);
title = TITLE ;
}
tcp:::send
/ ( ADDR == NULL || args[3]->tcps_raddr == ADDR ) /
{
nfs[args[1]->cs_cid]=1; /* this is an NFS thread */
this->delta= timestamp-walltime[args[1]->cs_cid];
walltime[args[1]->cs_cid]=timestamp;
this->flags="";
this->flags= strjoin((( args[4]->tcp_flags & TH_FIN ) ? "FIN|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_SYN ) ? "SYN|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_RST ) ? "RST|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_PUSH ) ? "PUSH|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_ACK ) ? "ACK|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_URG ) ? "URG|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_ECE ) ? "ECE|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_CWR ) ? "CWR|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags == 0 ) ? "null " : ""),this->flags);
printf("%5d %14s %6d %6d %6d %8d \ %-8s %8d %6d %8d %8d %8d %12d %s %d \n",
args[1]->cs_cid%1000,
args[3]->tcps_raddr ,
args[3]->tcps_snxt - args[3]->tcps_suna ,
args[3]->tcps_rnxt - args[3]->tcps_rack,
this->delta/1000,
args[2]->ip_plength - args[4]->tcp_offset,
"",
args[3]->tcps_swnd,
args[3]->tcps_snd_ws,
args[3]->tcps_rwnd,
args[3]->tcps_rcv_ws,
args[3]->tcps_cwnd,
args[3]->tcps_cwnd_ssthresh,
this->flags,
args[3]->tcps_retransmit
);
this->flags=0;
title--;
this->delta=0;
}
tcp:::receive
/ nfs[args[1]->cs_cid] && ( ADDR == NULL || args[3]->tcps_raddr == ADDR ) /
{
this->delta= timestamp-walltime[args[1]->cs_cid];
walltime[args[1]->cs_cid]=timestamp;
this->flags="";
this->flags= strjoin((( args[4]->tcp_flags & TH_FIN ) ? "FIN|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_SYN ) ? "SYN|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_RST ) ? "RST|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_PUSH ) ? "PUSH|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_ACK ) ? "ACK|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_URG ) ? "URG|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_ECE ) ? "ECE|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags & TH_CWR ) ? "CWR|" : ""),this->flags);
this->flags= strjoin((( args[4]->tcp_flags == 0 ) ? "null " : ""),this->flags);
printf("%5d %14s %6d %6d %6d %8s / %-8d %8d %6d %8d %8d %8d %12d %s %d \n",
args[1]->cs_cid%1000,
args[3]->tcps_raddr ,
args[3]->tcps_snxt - args[3]->tcps_suna ,
args[3]->tcps_rnxt - args[3]->tcps_rack,
this->delta/1000,
"",
args[2]->ip_plength - args[4]->tcp_offset,
args[3]->tcps_swnd,
args[3]->tcps_snd_ws,
args[3]->tcps_rwnd,
args[3]->tcps_rcv_ws,
args[3]->tcps_cwnd,
args[3]->tcps_cwnd_ssthresh,
this->flags,
args[3]->tcps_retransmit
);
this->flags=0;
title--;
this->delta=0;
}
输出看起来像(不是来自这种特殊情况):
cid ip usend urecd delta send recd ssz sscal rsz rscal congw conthr flags retran
320 192.168.100.186 240 0 272 240 \ 49232 0 1049800 5 1049800 2896 ACK|PUSH| 0
320 192.168.100.186 240 0 196 / 68 49232 0 1049800 5 1049800 2896 ACK|PUSH| 0
320 192.168.100.186 0 0 27445 0 \ 49232 0 1049800 5 1049800 2896 ACK| 0
24 192.168.100.177 0 0 255562 / 52 64060 0 64240 0 91980 2920 ACK|PUSH| 0
24 192.168.100.177 52 0 301 52 \ 64060 0 64240 0 91980 2920 ACK|PUSH| 0
一些标题
usend - unacknowledged send bytes
urecd - unacknowledged received bytes
ssz - send window
rsz - receive window
congw - congestion window
计划对 v3 和 v4 上的 dd 进行监听并进行比较。已经这样做了,但流量太大,我使用了磁盘文件而不是缓存文件,这使得比较时间变得毫无意义。将使用缓存数据运行其他监听,并且没有其他盒子之间的流量。待定
此外,网络人员表示,连接上没有流量整形或带宽限制器。
答案1
NFS 4.1(次要 1)旨在成为一种更快、更高效的协议,比以前的版本(尤其是 4.0)更值得推荐。
这包括客户端缓存,尽管与本场景无关,并行 NFS (pNFS)。主要的变化是协议现在是有状态的。
http://www.netapp.com/us/communities/tech-ontap/nfsv4-0408.html
根据 NetApps 的性能文档,我认为它是使用 NetApps 时推荐的协议。该技术类似于 Windows Vista+ 机会锁定。
NFSv4 与 NFS 的早期版本不同,它允许服务器将文件上的具体操作委托给客户端,从而实现更积极的客户端数据缓存并允许缓存锁定状态。服务器通过委托将文件更新和锁定状态的控制权交给客户端。这通过允许客户端执行各种操作并在本地缓存数据来减少延迟。目前有两种类型的委托:读取和写入。如果存在文件争用,服务器能够从客户端回调委托。一旦客户端拥有委托,它就可以对数据已在本地缓存的文件执行操作,以避免网络延迟并优化 I/O。委托产生的更积极的缓存在具有以下特征的环境中会大有帮助:
- 频繁打开和关闭
- 频繁的 GETATTR
- 文件锁定
- 只读共享
- 高延迟
- 快速客户端
- 服务器负载过重,客户端较多