NFS v3 против v4

11

Мне интересно, почему 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-400 МБ / с, но когда я меняю версию на vers=3, перемонтирую и перезапускаю дд, я получаю только 90 МБ / с . Файл, из которого я читаю, находится в памяти на сервере NFS. Обе стороны соединения - Solaris и имеют сетевой адаптер 10GbE. Я избегаю кэширования на стороне клиента, перемонтируя все тесты. Раньше я dtraceвидел на сервере, чтобы измерить, как быстро данные передаются через NFS. Для v3 и v4 я изменил:

 nfs4_bsize
 nfs3_bsize

от 32K до 1M по умолчанию (на v4 я увеличил скорость до 150 МБ / с с 32K) Я попытался настроить

  • nfs3_max_threads
  • clnt_max_conns
  • nfs3_async_clusters

улучшить производительность v3, но не идти.

На v3, если я запускаю четыре параллельных ddсервера, пропускная способность снижается с 90 МБ / с до 70-80 МБ, что заставляет меня поверить, что проблема в неком общем ресурсе, и если да, то мне интересно, что это такое, и могу ли я увеличить его ресурс.

код 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 и сравнение. Я уже сделал это, но было слишком много трафика, и я использовал файл на диске вместо кэшированного файла, что сделало сравнение времени бессмысленным. Запустит другой анализатор с кэшированными данными и без другого трафика между ящиками. TBD

Кроме того, ребята из сети говорят, что в соединениях нет ограничения трафика или ограничения пропускной способности.

Кайл Хейли
источник
2
Ну, во-первых, nfsv4 по умолчанию работает на tcp вместо udp.
Фил Холленбек
3
AFAIK, Solaris, в отличие от Linux, по умолчанию монтирует tcp даже на v3. Для тестов v3 я также явно "proto = tcp" в некоторых тестах, но имел одинаковую производительность на v3 с включением или без "proto = tcp"
Кайл Хейли
Вы уже включили гигантские кадры в инфраструктуре коммутации и сетевых адаптерах сервера?
полином
да, гигантские кадры установлены и проверены. С помощью dtrace я вижу размеры пакетов.
Кайл Хейли
1
На самом деле Linux также по умолчанию монтируется с tcp
janneb

Ответы:

4

NFS 4.1 (дополнительный 1) разработан для более быстрого и эффективного протокола и рекомендуется в предыдущих версиях, особенно в 4.0.

Это включает в себя кэширование на стороне клиента и, хотя это не относится к этому сценарию, параллельный NFS (pNFS) . Основное изменение заключается в том, что протокол теперь находится в состоянии.

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

Я думаю, что это рекомендуемый протокол при использовании NetApps, судя по их документации по производительности. Технология похожа на Windows Vista + оппортунистическая блокировка.

NFSv4 отличается от предыдущих версий NFS тем, что позволяет серверу делегировать определенные действия с файлом клиенту, чтобы обеспечить более агрессивное кэширование данных клиентом и разрешить кэширование состояния блокировки. Сервер передает управление обновлениями файлов и состоянием блокировки клиенту через делегирование. Это уменьшает задержку, позволяя клиенту выполнять различные операции и кэшировать данные локально. В настоящее время существует два типа делегаций: чтение и запись. Сервер имеет возможность отозвать делегирование от клиента в случае конфликта за файл. Как только клиент удерживает делегирование, он может выполнять операции с файлами, данные которых кэшированы локально, чтобы избежать задержек в сети и оптимизировать операции ввода-вывода. Более агрессивное кэширование, которое возникает в результате делегирования, может оказать большую помощь в средах со следующими характеристиками:

  • Частые открытия и закрытия
  • Частые GETATTR
  • Блокировка файла
  • Общий доступ только для чтения
  • Высокая задержка
  • Быстрые клиенты
  • Сильно загруженный сервер со многими клиентами
Стив-О
источник
Спасибо за указатели на NFS 4.1, хотя я AFAIK они мы на 4.0
Кайл Хейли
1
На самом деле, изменения в кэшировании на стороне клиента появились в версии 4.0 и могут быть самым большим различием в производительности для записи, как вы можете видеть из выдержки v4 - «NFSv4 ... делегировать ... клиенту». Просто заметил, что вопрос был о чтении. Я не уверен, насколько это актуально для этого случая.
Питер