NFS v3 versus v4

11

Ich frage mich, warum NFS v4 so viel schneller als NFS v3 ist und ob es in v3 Parameter gibt, die optimiert werden könnten.

Ich mounte ein Dateisystem

sudo mount  -o  'rw,bg,hard,nointr,rsize=1048576,wsize=1048576,vers=4'  toto:/test /test

und dann laufen

 dd if=/test/file  of=/dev/null bs=1024k

Ich kann 200-400 MB / s lesen, aber wenn ich die Version auf vers=3ändere, erneut einbinde und die dd erneut ausführe, erhalte ich nur 90 MB / s . Die Datei, aus der ich lese, ist eine In-Memory-Datei auf dem NFS-Server. Beide Seiten der Verbindung sind Solaris und verfügen über eine 10-GbE-Netzwerkkarte. Ich vermeide clientseitiges Caching, indem ich zwischen allen Tests erneut einbinde. Früher dtracehabe ich auf dem Server gesehen, wie schnell Daten über NFS bereitgestellt werden. Für v3 und v4 habe ich Folgendes geändert:

 nfs4_bsize
 nfs3_bsize

Von Standard 32K bis 1M (in Version 4 habe ich mit 32K maximal 150 MB / s erreicht) Ich habe versucht, Änderungen vorzunehmen

  • nfs3_max_threads
  • clnt_max_conns
  • nfs3_async_clusters

um die v3 leistung zu verbessern, aber kein go.

Auf v3 wenn ich vier parallel laufen lasse dd in Version 3 Durchläufe verwende, sinkt der Durchsatz von 90 MB / s auf 70 bis 80 MB, was mich zu der Annahme führt, dass es sich bei dem Problem um eine gemeinsam genutzte Ressource handelt. Wenn ja, frage ich mich, was es ist und ob ich das erhöhen kann Ressource.

dtrace-Code zum Abrufen der Fenstergrößen:

#!/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;
}

Die Ausgabe sieht so aus (nicht aus dieser speziellen Situation):

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

einige Überschriften

usend - unacknowledged send bytes
urecd - unacknowledged received bytes
ssz - send window
rsz - receive window
congw - congestion window

planen, snoop's der dd's über v3 und v4 zu nehmen und zu vergleichen. Habe es bereits getan, aber es gab zu viel Verkehr und ich habe eine Festplattendatei anstelle einer zwischengespeicherten Datei verwendet, was das Vergleichen von Timings bedeutungslos machte. Führt andere Snoop's mit zwischengespeicherten Daten und ohne weiteren Datenverkehr zwischen Boxen aus. TBD

Zusätzlich sagen die Netzwerk-Leute, dass es keine Traffic-Shaping- oder Bandbreitenbegrenzer für die Verbindungen gibt.

Kyle Hailey
quelle
2
Zum einen läuft nfsv4 standardmäßig auf tcp anstelle von udp.
Phil Hollenback
3
AFAIK, Solaris, montiert im Gegensatz zu Linux standardmäßig TCP sogar auf Version 3. Für v3-Tests habe ich in einigen Tests auch explizit "proto = tcp" verwendet, hatte aber die gleiche Leistung auf v3 mit oder ohne "proto = tcp"
Kyle Hailey
Haben Sie bereits Jumbo-Frames auf der Switching-Infrastruktur und den Server-NICs aktiviert?
Polynom
Ja, Jumbo-Frames werden eingerichtet und überprüft. Mit dtrace kann ich die Paketgrößen sehen.
Kyle Hailey
1
Tatsächlich
mounten

Antworten:

4

NFS 4.1 (Moll 1) ist als schnelleres und effizienteres Protokoll konzipiert und wird gegenüber früheren Versionen, insbesondere 4.0, empfohlen.

Dies umfasst clientseitiges Caching und, obwohl in diesem Szenario nicht relevant, Parallel-NFS (pNFS) . Die wichtigste Änderung besteht darin, dass das Protokoll jetzt zustandsbehaftet ist.

http://www.netapp.com/us/communities/tech-ontap/nfsv4-0408.html

Ich denke, es ist das empfohlene Protokoll bei der Verwendung von NetApps, gemessen an der Leistungsdokumentation. Die Technologie ähnelt der opportunistischen Sperrung von Windows Vista +.

NFSv4 unterscheidet sich von früheren Versionen von NFS dadurch, dass ein Server bestimmte Aktionen für eine Datei an einen Client delegieren kann, um ein aggressiveres Client-Caching von Daten und das Caching des Sperrstatus zu ermöglichen. Ein Server übergibt die Kontrolle über Dateiaktualisierungen und den Sperrstatus über eine Delegierung an einen Client. Dies reduziert die Latenz, indem der Client verschiedene Vorgänge ausführen und Daten lokal zwischenspeichern kann. Derzeit gibt es zwei Arten von Delegierungen: Lesen und Schreiben. Der Server kann eine Delegierung von einem Client zurückrufen, falls Konflikte um eine Datei auftreten. Sobald ein Client eine Delegierung besitzt, kann er Vorgänge für Dateien ausführen, deren Daten lokal zwischengespeichert wurden, um Netzwerklatenz zu vermeiden und die E / A zu optimieren. Das aggressivere Caching, das sich aus Delegierungen ergibt, kann in Umgebungen mit den folgenden Merkmalen eine große Hilfe sein:

  • Häufig öffnet und schließt
  • Häufige GETATTRs
  • Dateisperre
  • Schreibgeschütztes Teilen
  • Hohe Latenz
  • Schnelle Kunden
  • Stark ausgelasteter Server mit vielen Clients
Steve-o
quelle
Vielen Dank für die Hinweise auf NFS 4.1, obwohl ich AFAIK sie sind wir auf 4.0
Kyle Hailey
1
Tatsächlich kamen die clientseitigen Caching-Änderungen mit 4.0 und sind möglicherweise der größte Leistungsunterschied beim Schreiben, wie Sie aus dem v4-Auszug "NFSv4 ... delegieren ... an einen Client" ersehen können. Ich habe gerade bemerkt, dass es bei der Frage ums Lesen ging. Ich bin mir nicht sicher, wie relevant das meiste für diesen Fall ist.
Peter