NFS v3 contre v4

11

Je me demande pourquoi NFS v4 serait tellement plus rapide que NFS v3 et s'il y a des paramètres sur v3 qui pourraient être modifiés.

Je monte un système de fichiers

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

puis exécutez

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

Je peux lire 200-400MB / s mais quand je change de version vers=3, remonte et relance le dd je n'obtiens que 90MB / s . Le fichier que je lis est un fichier en mémoire sur le serveur NFS. Les deux côtés de la connexion sont Solaris et ont une carte réseau 10 GbE. J'évite toute mise en cache côté client en remontant entre tous les tests. J'avais l'habitude dtracede voir sur le serveur pour mesurer la vitesse à laquelle les données sont servies via NFS. Pour v3 et v4, j'ai changé:

 nfs4_bsize
 nfs3_bsize

de 32K par défaut à 1M (sur v4, j'ai atteint un maximum de 150 Mo / s avec 32K), j'ai essayé de peaufiner

  • nfs3_max_threads
  • clnt_max_conns
  • nfs3_async_clusters

pour améliorer les performances de la v3, mais non.

Sur la v3, si je lance quatre parallèles dd, le débit passe de 90 Mo / s à 70-80 Mo, ce qui m'amène à croire que le problème est une ressource partagée et si oui, alors je me demande ce que c'est et si je peux l'augmenter. Ressource.

code dtrace pour obtenir les tailles de fenêtre:

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

La sortie ressemble (pas à cette situation particulière):

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

quelques en-têtes

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

l'intention de prendre snoop's des dd sur v3 et v4 et de comparer. Je l'ai déjà fait, mais il y avait trop de trafic et j'ai utilisé un fichier disque au lieu d'un fichier mis en cache, ce qui rendait la comparaison des horaires sans signification. Exécute d'autres espions avec des données mises en cache et aucun autre trafic entre les boîtes. À déterminer

De plus, les gars du réseau disent qu'il n'y a pas de mise en forme du trafic ou de limiteurs de bande passante sur les connexions.

Kyle Hailey
la source
2
Eh bien, pour une chose, nfsv4 fonctionne par défaut sur TCP au lieu d'UDP.
Phil Hollenback
3
AFAIK, solaris, contrairement à linux, monte tcp par défaut même sur v3. Pour les tests v3, j'ai aussi explicitement "proto = tcp" dans certains tests mais j'ai eu les mêmes performances sur v3 avec ou sans "proto = tcp" inclus
Kyle Hailey
Avez-vous déjà activé des trames jumbo sur l'infrastructure de commutation et les cartes réseau du serveur?
polynôme
oui, les trames jumbo sont configurées et vérifiées. Avec dtrace, je peux voir les tailles de paquets.
Kyle Hailey
1
En fait, Linux par défaut est également monté avec
TCP

Réponses:

4

NFS 4.1 (mineur 1) est conçu pour être un protocole plus rapide et plus efficace et est recommandé par rapport aux versions précédentes, en particulier 4.0.

Cela inclut la mise en cache côté client et, bien que cela ne soit pas pertinent dans ce scénario, parallèle-NFS (pNFS) . Le principal changement est que le protocole est désormais avec état.

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

Je pense que c'est le protocole recommandé lors de l'utilisation de NetApps, à en juger par leur documentation sur les performances. La technologie est similaire à Windows Vista + verrouillage opportuniste.

NFSv4 diffère des versions précédentes de NFS en permettant à un serveur de déléguer des actions spécifiques sur un fichier à un client pour permettre une mise en cache client plus agressive des données et pour permettre la mise en cache de l'état de verrouillage. Un serveur cède le contrôle des mises à jour des fichiers et de l'état de verrouillage à un client via une délégation. Cela réduit la latence en permettant au client d'effectuer diverses opérations et de mettre en cache les données localement. Il existe actuellement deux types de délégations: lecture et écriture. Le serveur a la possibilité de rappeler une délégation d'un client en cas de conflit pour un fichier. Une fois qu'un client détient une délégation, il peut effectuer des opérations sur des fichiers dont les données ont été mises en cache localement pour éviter la latence du réseau et optimiser les E / S. La mise en cache plus agressive qui résulte des délégations peut être d'une grande aide dans les environnements avec les caractéristiques suivantes:

  • Ouverture et fermeture fréquentes
  • GETATTRs fréquents
  • Verrouillage de fichiers
  • Partage en lecture seule
  • Latence élevée
  • Clients rapides
  • Serveur lourdement chargé avec de nombreux clients
Steve-o
la source
Merci pour les pointeurs sur NFS 4.1 bien que je AFAIK ils nous sommes sur 4.0
Kyle Hailey
1
En fait, les changements de mise en cache côté client sont venus avec 4.0, et peuvent être la plus grande différence de performances, pour l'écriture, comme vous pouvez le voir dans l'extrait v4 - "NFSv4 ... déléguer ... à un client". Je viens de remarquer que la question portait sur la lecture. Je ne sais pas dans quelle mesure la plupart de ces informations sont pertinentes dans ce cas.
Peter