NFS v3 versus v4

11

Gostaria de saber por que o NFS v4 seria muito mais rápido que o NFS v3 e se há algum parâmetro na v3 que possa ser ajustado.

Eu monto um sistema de arquivos

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

e depois corra

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

Posso ler 200-400MB / s, mas quando mudo de versão para vers=3remontar e executar novamente o dd, recebo apenas 90MB / s . O arquivo que estou lendo é um arquivo de memória no servidor NFS. Ambos os lados da conexão são Solaris e têm 10GbE NIC. Evito qualquer cache do lado do cliente remontando entre todos os testes. Eu costumava dtracever no servidor para medir a rapidez com que os dados são servidos via NFS. Para as v3 e v4, mudei:

 nfs4_bsize
 nfs3_bsize

do padrão de 32K a 1M (na v4 eu atingi 150MB / s com 32K) tentei ajustar

  • nfs3_max_threads
  • clnt_max_conns
  • nfs3_async_clusters

para melhorar o desempenho da v3, mas não é possível.

Na v3, se eu rodar quatro paralelos dd, a taxa de transferência diminui de 90 MB / se 70-80 MB, o que me leva a acreditar que o problema é algum recurso compartilhado. recurso.

código dtrace para obter tamanhos de janela:

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

Saída parece (não a partir desta situação específica):

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

alguns cabeçalhos

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

planejando tomar bisbilhoteiros dos dd's sobre v3 e v4 e comparar. Já o fiz, mas havia muito tráfego e usei um arquivo de disco em vez de um arquivo em cache, o que fez a comparação de tempos sem sentido. Irá executar outros bisbilhoteiros com dados em cache e nenhum outro tráfego entre caixas. TBD

Além disso, os funcionários da rede dizem que não há modelagem de tráfego ou limitadores de largura de banda nas conexões.

Kyle Hailey
fonte
2
Bem, por uma coisa, o nfsv4 é executado no tcp por padrão, em vez do udp.
Phil Hollenback
3
AFAIK, solaris, ao contrário do linux, monta o tcp por padrão, mesmo na v3. Para v3 testes eu também explicitamente "proto = tcp" em alguns dos testes, mas teve o mesmo desempenho em v3 com ou sem incluindo "proto = tcp"
Kyle Hailey
Você já ativou os jumbo-frames nas NICs da infraestrutura de comutação e do servidor?
polinomial
sim, os jumbo-frames são configurados e verificados. Com o dtrace, posso ver os tamanhos dos pacotes.
precisa saber é o seguinte
1
Na verdade, Linux também usa como padrão de montagem com tcp
janneb

Respostas:

4

O NFS 4.1 (menor 1) foi projetado para ser um protocolo mais rápido e eficiente e é recomendado em versões anteriores, especialmente na 4.0.

Isso inclui o cache do lado do cliente e , embora não seja relevante nesse cenário, o NFS paralelo (pNFS) . A principal mudança é que o protocolo agora é estável.

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

Eu acho que é o protocolo recomendado ao usar o NetApps, a julgar pela documentação de desempenho. A tecnologia é semelhante ao Windows Vista + bloqueio oportunista.

O NFSv4 difere das versões anteriores do NFS, permitindo que um servidor delegue ações específicas em um arquivo a um cliente para permitir o armazenamento em cache mais agressivo dos dados e o armazenamento em cache do estado de bloqueio. Um servidor cede o controle de atualizações de arquivos e o estado de bloqueio para um cliente por meio de uma delegação. Isso reduz a latência, permitindo que o cliente execute várias operações e armazene dados em cache localmente. Atualmente existem dois tipos de delegações: leitura e gravação. O servidor tem a capacidade de retornar uma delegação de um cliente, caso haja uma disputa por um arquivo. Depois que um cliente mantém uma delegação, ele pode executar operações em arquivos cujos dados foram armazenados em cache localmente para evitar a latência da rede e otimizar a E / S. O cache mais agressivo resultante das delegações pode ser uma grande ajuda em ambientes com as seguintes características:

  • Abre e fecha com frequência
  • GETATTRs frequentes
  • Bloqueio de arquivo
  • Compartilhamento somente leitura
  • Alta latência
  • Clientes rápidos
  • Servidor fortemente carregado com muitos clientes
Steve-o
fonte
Obrigado pelas dicas sobre NFS 4.1 Embora eu AFAIK que estamos no 4.0
Kyle Hailey
1
Na verdade, as alterações no cache do cliente vieram com a 4.0 e podem ser a maior diferença de desempenho, por escrito, como você pode ver no extrato da v4 - "NFSv4 ... delegar ... a um cliente". Só notei que a pergunta era sobre leitura. Não tenho certeza do quanto isso é relevante para esse caso.
Peter