フォルダー内の数百万の(小さな)テキストファイル


15

Linuxファイルシステムに数百万のテキストファイルを保存し、圧縮して任意のコレクションをサービスとして提供できるようにします。キー/値データベースのような他のソリューションを試しましたが、並行性と並列性の要件により、ネイティブファイルシステムを使用することが最良の選択です。

最も簡単な方法は、すべてのファイルをフォルダーに保存することです。

$ ls text_files/
1.txt
2.txt
3.txt

これ、フォルダ内のファイル数に制限のないEXT4ファイルシステム可能です。

2つのFSプロセスは次のとおりです。

  1. Webスクレイプからテキストファイルを書き込みます(フォルダー内のファイルの数に影響されません)。
  2. ファイル名のリストで指定された、選択したファイルを圧縮します。

私の質問は、1つのフォルダーに最大1,000万個のファイルを保存すると、上記の操作のパフォーマンスや一般的なシステムパフォーマンスに影響しますか?


4
関連:デバイスに十分なスペースがある場合、mv中に断続的な「デバイスにスペースがありません」エラーを修正する方法。を使用するとdir_index、多くの場合デフォルトで有効になり、検索が高速になりますが、ディレクトリごとのファイル数が制限される場合があります。
マークプロトニック

仮想マシンですぐに試して、それがどのようなものかを見てみませんか?bashを使用すると、ランダムな文字が含まれた無数のテキストファイルをフォルダーに追加するのは簡単です。ここで学習することに加えて、そのようにして本当に役立つ情報を得ることができると思います。
ジョシュア

2
@JoshuaD:あなたは、新鮮なFSに一度にすべてを移植する場合は、ディスク上の連続なので、すべてのiノードを持つようになりそうだls -lか、何か他statのディレクトリ内のすべてのiノード(例えばねbash、人工的に速くなりますグロブ/タブ補完を)いくつかの消耗の後(いくつかのファイルを削除し、いくつかの新しいファイルを書き込みます)よりも。これは、XFSがext4の方がXFSよりも優れている可能性があります。XFSがiノードとデータに動的にスペースを割り当てるため、iノードがより多く散らばってしまうためです。(しかし、それは非常に少ない詳細な知識に基づいた純粋な推測です;私はかろうじてext4を使用しました)。abc/def/サブディレクトリに移動します。
ピーターコーデス

ええ、私が提案したテストはOPに「これは機能します」と伝えることができるとは思いませんが、「これは機能しません」とすぐに彼に伝えることができます。
ジョシュア

1
しかし、並行性と並列性の要件により、ネイティブファイルシステムを使用することが最良の選択になり ます。率直に言って、MySQLのようなローエンドのRDBMSやzipファイルをオンザフライでZipOutputStream作成するJavaサーブレットでさえ、無料のLinuxネイティブファイルシステムのほとんどに勝ると思います。IBMのGPFSに支払いたいとは思いません。JDBC結果セットを処理してzipストリームを作成するループは、おそらく6〜8行のJavaコードです。
アンドリューヘンレ

回答:


10

lsコマンド、あるいはタブ補完またはシェルによってワイルドカード展開は、通常、アルファベット順に、それらの結果を提示します。これには、ディレクトリリスト全体を読み取ってソートする必要があります。1つのディレクトリに1,000万個のファイルがあるため、このソート操作には無視できない時間がかかります。

TAB補完の衝動に抵抗し、zip圧縮するファイルの名前を完全に書き込めるなら、問題はないはずです。

ワイルドカードに関する別の問題は、ワイルドカードの展開が、最大長のコマンドラインに収まるよりも多くのファイル名を生成する可能性があることです。通常のコマンドラインの最大長は、ほとんどの状況で十分な長さですが、1つのディレクトリ内の数百万のファイルについて話している場合、これはもはや安全な仮定ではありません。ワイルドカード拡張でコマンドラインの最大長を超えると、ほとんどのシェルはコマンドライン全体を実行せずに失敗します。

これは、find次のコマンドを使用してワイルドカード操作を行うことで解決できます。

find <directory> -name '<wildcard expression>' -exec <command> {} \+

または可能な限り同様の構文。find ... -exec ... \+自動的にアカウントに最大コマンドライン長さを取るし、各コマンドラインにファイル名の最大量をフィッティングしながら必要な回数だけコマンドを実行します。


最新のファイルシステムは、B、B +、または類似のツリーを使用してディレクトリエントリを保持します。en.wikipedia.org/wiki/HTree
dimm

4
はい...しかし、シェルまたはlsコマンドがディレクトリのリストがすでにソートされていることを知らない場合は、とにかくソートアルゴリズムを実行するのに時間がかかります。さらに、ユーザースペースは、ファイルシステムが内部で行うこととは異なるローカライズされたソート順(LC_COLLATE)を使用している可能性があります。
telcoM

17

これは、意見に基づく質問/回答に危険に近いものですが、いくつかの事実を自分の意見とともに提供しようとします。

  1. フォルダーに非常に多くのファイルがある場合、それらを列挙しようとするシェルベースの操作(例mv * /somewhere/else:)は、ワイルドカードの展開に失敗するか、結果が大きすぎて使用できない可能性があります。
  2. ls 少数のファイルよりも非常に多数のファイルを列挙するのに時間がかかります。
  3. ファイルシステムは単一のディレクトリで数百万のファイルを処理できますが、おそらく苦労するでしょう。

1つの推奨事項は、ファイル名を2、3、または4文字のチャンクに分割し、それらをサブディレクトリとして使用することです。たとえば、somefilename.txtとして保存される場合がありますsom/efi/somefilename.txt。数値名を使用している場合は、左から右ではなく右から左に分割して、より均等に分布するようにします。たとえば、として12345.txt保存され345/12/12345.txtます。

同等のものを使用zip -j zipfile.zip path1/file1 path2/file2 ...して、ZIPファイルに中間サブディレクトリパスを含めないようにすることができます。

これらのファイルをWebサーバーから提供している場合(関連があるかどうかはわかりません)、Apache2の書き換えルールで仮想ディレクトリを優先してこの構造を非表示にするのは簡単です。Nginxについても同じことが当てはまります。


*あなたはメモリが不足していない限り拡張は成功しますが、あなたは(Linux上)スタックサイズの上限を引き上げるか、シェルを使用しない限り、mv組み込みであるか(は、ksh93、zshの)組み込み可能に、execve()システムコールがE2BIGエラーで失敗することがあります。
ステファンシャゼル

@StéphaneChazelasはい、私の言葉の選択はもっと良かったかもしれませんが、ユーザーにとっての最終的な効果はほとんど同じです。複雑さに行き詰まることなく、単語を少し変更できるかどうかを確認します。
ロアイマ

議論する問題に遭遇することなく、中間のサブディレクトリパスを含めることを避けた場合、そのzipファイルを圧縮解除する方法に興味がありますか?
タコ

1
@Octopus OPは、zipファイルに「選択されたファイル、ファイル名のリストで指定」が含まれると述べています。
ロアイマ

zip -j - ...出力ストリームを直接使用し、を介してクライアントのネットワーク接続にパイプすることをお勧めしますzip -j zipfile.zip ...。実際のzipファイルをディスクに書き込むことは、データパスがdisk-> compress-> write to disk-> read from disk-> send to clientから読み取られることを意味します。これにより、ディスクからの読み取り->圧縮->クライアントへの送信よりも、ディスクIO要件が最大3倍になります。
アンドリューヘンレ

5

映画、テレビ、ビデオゲームのデータベースを処理するWebサイトを運営しています。これらのそれぞれについて、番組ごとに数十の画像を含むテレビの複数の画像があります(つまり、エピソードのスナップショットなど)。

最終的には、多くの画像ファイルになります。250,000以上の範囲内。これらはすべて、アクセス時間が妥当なマウントされたブロックストレージデバイスに格納されます。

画像を保存する最初の試みは、 /mnt/images/UUID.jpg

私は次の課題に直面しました。

  • lsリモート端末経由でハングします。プロセスはゾンビにCTRL+Cなり、破壊されません。
  • そのポイントに到達する前に、lsコマンドは出力バッファーをすぐにいっぱいにしCTRL+C、無限スクロールを停止しません。
  • 1つのフォルダーから250,000個のファイルを圧縮するには、約2時間かかりました。端末から切り離されたzipコマンドを実行する必要があります。そうしないと、接続が中断した場合は、最初からやり直す必要があります。
  • Windowsでzipファイルを使用しようとするリスクはありません。
  • フォルダーはすぐに人間が許可ないゾーンになりました。

パスを作成するために作成時間を使用して、サブフォルダーにファイルを保存することになりました。など/mnt/images/YYYY/MM/DD/UUID.jpg。これにより、上記のすべての問題が解決され、日付を対象としたzipファイルを作成できました。

持っているファイルの唯一の識別子が数値であり、これらの番号が順番に実行される傾向がある場合。なぜしないことにより、グループにそれらを100000100001000

たとえば384295.txt、パスという名前のファイルがある場合:

/mnt/file/300000/80000/4000/295.txt

数百万に達するとわかっている場合。01,000,000のプレフィックスを使用

/mnt/file/000000/300000/80000/4000/295.txt

1

Webスクレイプからテキストファイルを書き込みます(フォルダー内のファイルの数に影響されません)。

新しいファイルを作成するには、ディレクトリファイルをスキャンして、新しいディレクトリエントリに十分な空きスペースを探す必要があります。新しいディレクトリエントリを格納するのに十分なスペースが見つからない場合、ディレクトリファイルの末尾に配置されます。ディレクトリ内のファイルの数が増えると、ディレクトリをスキャンする時間も長くなります。

ディレクトリファイルがシステムキャッシュに残っている限り、これによるパフォーマンスヒットは悪くありませんが、データが解放された場合、ディスクからディレクトリファイル(通常は非常に断片化されている)を読み取るにはかなりの時間がかかります。SSDはこれを改善しますが、数百万のファイルがあるディレクトリの場合、顕著なパフォーマンスヒットが依然として存在する可能性があります。

ファイル名のリストで指定された、選択したファイルを圧縮します。

また、数百万のファイルがあるディレクトリで追加の時間が必要になる可能性があります。ハッシュされたディレクトリエントリ(EXT4など)を持つファイルシステムでは、この違いは最小限です。

フォルダーに最大1,000万個のファイルを保存すると、上記の操作のパフォーマンス、または一般的なシステムパフォーマンスに影響しますか?

サブフォルダーのツリーには、上記のパフォーマンス上の欠点はありません。さらに、基礎となるファイルシステムがハッシュされたファイル名を持たないように変更された場合、ツリー方法論は依然としてうまく機能します。


1

まず、「ls」が「ls -U」でソートされないようにし、〜/ bashrcを「alias ls = "ls -U"」などに更新します。

大きなファイルセットの場合、次のようにしてこれを試すことができます。

  • テストファイルのセットを作成する

  • 多くのファイル名が問題を引き起こすかどうかを確認します

  • xargs parmeter-batchingおよびzipの(デフォルト)動作を使用して、ファイルをzipに追加して問題を回避します。

これはうまくいきました:

# create ~ 100k files
seq 1 99999 | sed "s/\(.*\)/a_somewhat_long_filename_as_a_prefix_to_exercise_zip_parameter_processing_\1.txt/" | xargs touch
# see if zip can handle such a list of names
zip -q /tmp/bar.zip ./*
    bash: /usr/bin/zip: Argument list too long
# use xargs to batch sets of filenames to zip
find . -type f | xargs zip -q /tmp/foo.zip
l /tmp/foo.zip
    28692 -rw-r--r-- 1 jmullee jmullee 29377592 2017-12-16 20:12 /tmp/foo.zip
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.