start

SambaとZFSで大量のファイルを扱う時はcase-sensitiveを最適化する

同一フォルダに大量のファイルがあるとSambaが超遅くなる問題、自分なりの知見が得られたのでメモ。結果だけ知りたい人は最後までスクロールしてくだしあ。

ことの発端は、数万個のファイルがあるフォルダをSambaのファイルサーバにコピーすると、速度が10kB/s前後まで低下する現象に見舞われた。速度はコピーが進むごとに低下し、比例してsmbdのCPU占有率が上がるというのが特徴。サーバ上で直接コピーすると何の問題もない。

調査を進めると、Sambaはファイル名の重複チェックのため、ファイル作成時に作成先フォルダ内の全ファイル名を検査することが分かった。この時に行われる、ファイル名の大文字/小文字変換と比較処理がボトルネックとなっているようだ。Windowsでは歴史的にファイル名の大文字/小文字を区別しない1)が、UNIX系ではOS/ファイルシステム共に大文字/小文字を区別することが多い。このWindowsとUNIXの違いをSambaで吸収してやる必要があるわけだ。

プロファイルしたわけでもソースコードを見たわけでもないので確証はないが、Sambaのドキュメントに「ディレクトリに大量のファイルがある特殊なケースでは、case sensitiveをyesにせよ」(抄訳)と書かれており、ファイルコピーの進捗にあわせ指数関数的に速度が落ちる(CPU負荷が上がる)という挙動から、当たらずとも遠からずだと思う。

ではcase sensitive = yesにすれば万事解決かといえば、そう簡単な話ではない。

前述の通りWindowsはFS的にはcase-sensitiveないしcase-preservingだが、表面的にはcase-insensitiveとして振る舞う。この仕様に胡坐をかき、ファイルパスを内部的に大文字または小文字に変換して扱うアプリケーションが少なくない。例えば、本来のファイルパスはC:\Data\FILE.datにもかかわらず、アプリケーション内部ではc:\data\file.datとして扱うことが往々にある。(自分もそういう処理をつい書いちゃうんだけど(;'∀'))

ローカルのファイルに対してなら、Windowsがよしなに取り計らってくれるので問題にはならない。

しかし、共有フォルダのアクセスに関しては、忖度することなくリクエストされたファイルパスをそのままサーバに渡しているようだ。ゆえにサーバ側でそのあたりが考慮されてないと、意図したファイルが見つからないという事になる。Sambaのcase sensitiveオプションは、まさにその辺の制御に関するオプションなのだ。

case sensitive=yesにするということは、\\Server\Data\FILE.dat\\Server\data\file.datは別のファイル扱いになるということで、手抜きアプリからすれば、case-insensitiveなら見えていたファイルが見つからなくなる。これでは使い物にならない2)

そこでZFSのcasesensitivityプロパティの登場だ。

その名の通り、ファイル名の大文字/小文字の取り扱いに関するプロパティで、デフォルト値はcasesensitiveである。

これをcaseinsensitiveにしてやれば、大文字/小文字を区別しないようにファイルシステムが処理するようになる。“insensitive”とはなっているけど、挙動としては“preserving”のようだ。ちなみに、mixedというのもあるが、使いどころがよくわからない挙動なので指定しない方が無難(一応、Windowsを想定した挙動らしいんだけど…)。

残念ながら、というか性質を考えたら仕方ないけど、本プロパティはデータセット作成時にしか指定できない。既に問題が起きてしまっている場合は、caseinsensitiveなデータセットを新たに作り、casesensitiveの方からファイルコピーで移行するしかない。(zfs send/recvではプロパティが引き継がれるので意味がない。)

そのうえでSamba側のcase sensitive=yesとしてやれば、smbdの負荷問題は解決する。

Sambaがファイル作成時にファイル名を全舐めしてしているのは、対象フォルダに対する変更をSamba側で検知する術がないからだと思われる。Sambaがファイルを作成しようとしたまさにその時、同名のファイルがサーバのローカルで作られたりする可能性があるから、キャッシュではファイル名のユニーク性を担保できず、都度確認せざるを得ないのだろう。

一方、ファイルシステムレベルなら、ファイル名の一意性やアトミック性の保証が効率的に行えているだろうという目論見。

結論としてはSambaとZFSで以下の設定を行うと幸せになれる。ZFSじゃない人はスマン…

  • ZFS
    • ファイル共有用にcasesensitivity=caseinsensitiveなFSを作る
      zfs create -o casesnsitivity=caseinsensitive ztank/path/to/cifs

  • Samba
    • ファイル名の扱いをcase-sensitiveにする

      case sensitive = yes
      case preserve = yes
      short preserve case = yes

(2021-05-17 追記)

上記設定について、以前はcase preserve, short preserve caseをnoとしていたが、yesの方が良さそうである。これらはファイル新規作成時のファイル名の大文字・小文字の取り扱いのオプションで、noだと大文字ないし小文字に変換されていまう(default caseの指定に因る。デフォルト値はlowerなので小文字になる。)


1)
ファイルシステム上は区別して保存されている。この差はAPIで吸収され傍目からはcase-preservingのように見える
2)
実際、某有名3Dモデリングソフトでアセットファイルが突然見えなくなるという現象に遭遇している

Samba 4.7.4 on NAS4Free 11.1.0.4がメモリバカ食いする原因がわかった

知人NASのCIFS共有が調子良くない問題の調査中、smbdがアホほどメモリ確保する現象に見舞われた。1プロセスあたりギガ単位で消費し、最終的に16GBの物理メモリとスワップ64GBを食いつぶし、強制的に電源落とすしかなくなるなんて、どう考えても異常だ。smbdがメモリをバカ食いするせいでARCにメモリが割り当てられず、ストレージの実効性能が上がらないのもCIFSが遅い問題の遠因になってると思われる。

Sambaのオプションをあれこれイジった所、シャドウコピーを有効にするとダメっぽい。下図がシャドウコピーオプション有無でのtopを比較したものだ。

もうね、笑っちゃうくらい一目瞭然。メモリ消費量が文字通り桁違いである。シャドーコピー有効の方は、1日足らずでメモリ枯渇しかけてるのに対し、無効の方は4日程度経過のロードアベレージが13行くほどの負荷が掛かってるのに余裕のよっちゃんイカ。(ちなみにこの状態でもつつがなくファイル共有が行われている。)

Sambaでシャドーコピーってことは、vfs_shadow_copy2モジュールあたりがバグってんのかしらね?NAS4Free 9か10の頃は、シャドーコピー有効でもこんなこと起こらなかった記憶があるんだけど…。(超おぼろげなので保証はできない。)

Samba 4.6のファイルコピーがCPU 100%近く使い超絶遅い件

この状態に悩まされてる人は何よりも答えが欲しいだろうから、最初に結論を書いておくと、smb.confでcase sensitive = yesと設定すると多分直る。直下に大量のファイルを抱えたフォルダをコピーするとsmbdがCPUを100%近く消費し、コピー速度が極めて遅くなる現象ならほぼ間違いなく直る。

(2018-07-03 追記)
case sensitive = yesの副作用を十分に考慮のこと。一例として、エクスプローラでは見えるファイルが、特定のアプリケーションでは存在しないファイルになるといった不具合が出るなど。詳細はこちら

「”大量のファイル”ってどれくらい?」かというと、サーバマシンの性能にもよるがCore i系なら概ね1万ファイル、流行りのラズパイとかだと恐らくもっと少数、単純なクロック比で1/2、実性能はもっと劣るだろうから更に半分で2500ファイルくらい?。完全な当てずっぽうですけど。要はファイル名の比較のところがボトルネックになっているようなので、CPUのシングルスレッド性能に依存する。

例によってNAS4Free 11.0.0.4 (4303)[FreeBSD 11.0-RELEASE-p10/Samba 4.6.4]でNASをでっち上げたのだが、知人曰く、今までのNASに較べてファイルコピーが遅い、と。マシンのスペック的にはXeon E5-2620v3, メモリ8GB, 1GbE×2のLAGで、ストレージも3.5インチHDD2本でRAID-1のペアを3ペアでRAID10なので問題が出るとは考えにくい。実際、シェルでのシーケンシャルライトでは500MB/sくらい出てる。

様々なサイズのテストファイルを試してみるも、至って正常な速度が出るし、むしろ他のNASよりも速いくらい。ところが、知人が遅いというファイル群で試してみると、たしかに遅い。小さなファイルが多いためワイヤーレートには程遠いが、それでも初速は10MB/s超えてるのに、あれよあれよと速度が落ちて仕舞には100kB/sを切ってしまう。そして何故かsmbdがCPUを100%近く持っていく尋常ならざる状態に。Pen!!!時代のGbEじゃあるまいし、たかがファイル転送でCPU 100%ってどんだけー。

問題のファイル群をシェルでcpすると30MB/s出てるので、やっぱりマシンに問題はなさげ。さらに、NAS4Free 9 [FreeBSD 9.3-RELEASE-p14/Samba 4.1.18]搭載の別マシン(Xeon E3-1225v2/16GB/3.5“ HDD 6台でRAID-Z2なので性能は必要十分)で試すと、安定して数MB/sは出るし、CPU負荷も常識的な範囲。となれば、問題なのはSambaっぽい…?

ここまで絞り込んでからが大変だった。

情報がないない。マシンの省電力設定を切ってみたり、Sambaが速くなる各種おまじないを試してみたり、SMBのプロトコルバージョンを変えたり、LAGを解除してみたり、etc…するも効果なし。ググりにググって、ようやくFreeNASのフォーラムでcase sensitiveが原因じゃねという投稿を見つけた次第。

早速case sensitive = yesにして試してみたら、効果てきめん。転送速度もCPU負荷も劇的に改善された(テストファイルが32kBなので速度がそんなに出てないのは仕方ない)。論より証拠ってなもんで、比較画像貼っておきますね。

 Samba 4.6.4のcase sensitive設定によるファイルコピー速度の比較

その後、Sambaのドキュメントのcase sensitive設定のところを見たら、思いっきり「As a special case for directories with large numbers of files, if the case options are set as follows, “case sensitive = yes”, (後略)」と書いてあったでござる(´・ω・`)。

大文字小文字の変換処理ごときで遅くなるなよ!と思わなくもないが、ファイルの新規作成時はディレクトリ内の既存ファイル名と被ってないか総当りでチェックしているようなので、何も考えずに実装すれば計算量はO(n/^2^/)、ファイル数が増えると爆発的に比較数が増えるんすなぁ…。にしてもですよ、デフォルト設定のcase sensitive = autoは以前のバージョンから変わってないわけで、いきなり遅くなるなんてチョーひどくなーい?

case sensitive設定を変えるとWindowsからのアクセスに支障がでないか心配なところだが(なんたってWindowsは表面上は大文字小文字区別しませんからね!)、/-そこはエクスプローラが上手いこと取り計らってくれる模様。本当かどうかは知らない。-/ → (2019-05-19 追記)取り計らってくれなかった。例えばa.txtとA.TXTがあった場合、どちらのファイルを見に行くかは半固定。一度決まった同名ファイルのアクセス先は固定されるが、それらファイルを含むディレクトリの内容が変わるとアクセス先の選定が再度行われ、その結果は不定ってのがWindowsの昔からの仕様らしい…。

(2018-07-03 追記)
再度の注意。アプリケーションによっては意図しない挙動が発生するためcase sensitive = yesでの運用は十分なテストを行うこと!

Netatalk 3でsendfileを無効にするとCannot allocate memoryが起きる?

(2016-01-04 追記)
どうやらVirtualBoxが原因だった模様。詳細→FreeBSDでVirtualBoxを動かしていると巨大ファイルの転送でCannot allocate memoryが出る

いつ頃からから、Netatalk 3.1.7で提供しているボリュームとの接続が突然切れる現象が起きるようになった。

正確な発症条件は不明だが、ストレージの読み書き負荷が高い時や、巨大な動画ファイルでシークすると発生しやすい印象。いずれにせよ、何の前触れもなく発生し接続断のダイアログも出ないので地味にストレスが溜まる。

接続が切れる直前は決まって、ログにdsi_stream_sendafp_read_extのCannot allocate memoryが記録されている。

Jun 02 23:55:18.897112 afpd[57517] {afp_dsi.c:624} (debug:AFPDaemon): <== Start AFP command: AFP_READ_EXT
Jun 02 23:55:18.897126 afpd[57517] {fork.c:829} (debug:AFPDaemon): afp_read(fork: 422 [data], off: 1310720, len: 65536, size: 2222591)
Jun 02 23:55:18.897156 afpd[57517] {fork.c:880} (debug:AFPDaemon): afp_read(name: "01 プロローグ・静かなる侵略.m4a", offset: 1310720, reqcount: 65536): got 65536 bytes from file
Jun 02 23:55:18.897170 afpd[57517] {dsi_read.c:29} (maxdebug:DSI): dsi_readinit: sending 65536 bytes from buffer, total size: 65536
Jun 02 23:55:18.897183 afpd[57517] {dsi_stream.c:530} (maxdebug:DSI): dsi_stream_send(65536 bytes): START
Jun 02 23:55:18.897353 afpd[57517] {dsi_stream.c:564} (error:DSI): dsi_stream_send: Cannot allocate memory
Jun 02 23:55:18.897375 afpd[57517] {fork.c:913} (error:AFPDaemon): afp_read(01 プロローグ・静かなる侵略.m4a): Cannot allocate memory
Jun 02 23:55:18.897397 afpd[57517] {dsi_stream.c:281} (maxdebug:DSI): dsi_stream_write(send: 18 bytes): START
Jun 02 23:55:18.897419 afpd[57517] {dsi_stream.c:325} (maxdebug:DSI): dsi_stream_write(send: 18 bytes): END
(中略)
Jun 02 23:55:18.898533 afpd[57517] {cnid_dbd.c:498} (debug:CNID): closing database connection for volume 'Decomo'
Jun 02 23:55:18.898578 cnid_dbd[57522] {comm.c:207} (maxdebug:CNID): comm_rcv: got data on fd 5
Jun 02 23:55:18.898980 afpd[57517] {afp_dsi.c:108} (note:AFPDaemon): AFP statistics: 3177.09 KB read, 2524196.96 KB written
Jun 02 23:55:18.899005 afpd[57517] {dircache.c:615} (info:AFPDaemon): dircache statistics: entries: 2088, lookups: 10018, hits: 7503, misses: 2463, added: 2140, removed: 52, expunged: 52, evicted: 0
Jun 02 23:55:18.899020 afpd[57517] {dsi_stream.c:530} (maxdebug:DSI): dsi_stream_send(0 bytes): START
Jun 02 23:55:18.899033 afpd[57517] {dsi_stream.c:538} (maxdebug:DSI): dsi_stream_send(16 bytes): DSI header, no data
Jun 02 23:55:18.899045 afpd[57517] {dsi_stream.c:281} (maxdebug:DSI): dsi_stream_write(send: 16 bytes): START
Jun 02 23:55:18.899068 afpd[57517] {dsi_stream.c:325} (maxdebug:DSI): dsi_stream_write(send: 16 bytes): END
Jun 02 23:55:18.899089 afpd[57517] {afp_dsi.c:137} (info:AFPDaemon): Connection terminated
Jun 02 23:55:18.899899 afpd[57498] {main.c:149} (info:AFPDaemon): child[57517]: exited 1

Mac側はOS X v10.9.5で、サーバ側はFreeBSD 10.1-RELEASE。10.1RにはZFSのARCがメモリを食いつぶすバグがあるらしく、それが原因かと思って10-STABLEに更新するも、症状は相変わらず。

で、試行錯誤を続けていたが、Netatalk 3のsendfileを有効にしたらピタッとおさまった。正確には未だ出るけど、Netatalkとの接続は維持自体はされるようになった(自動で再接続されている模様)。おぼろげな記憶をたどると「ZFS環境でApache等のsendfileを有効にするとキャッシュの二重持ちになる可能性がある」という資料を見て、sendfile無効でNetatalk 3を作り直してたような気がしないでもなく、丁度その辺から問題が出たような気がする……。

時を同じくして、同サーバのSamba 4でファイル一覧の取得がタイムアウトしたり、動画が全く再生できなくなったりしていたのだが、こちらは逆にuse sendfile = yesを無効化したら直った。

全く以て謎だ…

SambaマシンがWindowsの「ネットワーク」に表示されない時の対処方法

折角設定したSambaマシンが、どう頑張ってもWindowsのネットワーク一覧に表示されない時がある。

そんな時はsmb.confのglobalセクションに次の2行を追加し、smbdを再起動すればおk。

local master = yes
os level = 200

Windowsのフォルダ共有では、同一ネットワーク内でフォルダ共有が有効なマシンのどれかがマスターブラウザとなり、ホスト名とIPアドレスの広告を行っているそうだ。で、たまにこの情報が壊れたまま広告し続ける事があるらしく、そうするとマシンが一覧に出ないという事態に陥る。どのマシンがマスターとなるかは不定だが、より新しいWindowsが選ばれるのだそうな。

なので、上記設定でSambaのマスターブラウザ化を許可し、OSのバージョンを偽装することで、確実にSambaがマスターブラウザになれるようにしている。os levelの値は最大255だけど、最大値だと不安(プログラマあるある)なので少し控えめに200としている。

取り敢えず、うちの環境では正しく表示されるようになった。

  • start.txt
  • 最終更新: 2022-07-27 15:26
  • by Decomo