gzipを使用して圧縮されたnginxログイン36GBファイルがあります。この小さなファイルをメモリ不足なしで私が書く別のスクリプトの入力として使用できるように、このファイルをより小さなファイルに分割したいと思います。ファイルの最初の列にはIPアドレスが含まれており、特定のIPアドレスからのすべての要求は分割後に同じファイルに存在する必要があります。
現在達成すべきことは次のとおりです。
#!/bin/bash
FILENAME=$1
NUM_FILES=$(($2))
PREFIX=$3
unpigz -c $FILENAME | awk -v NUM_FILES=$NUM_FILES -v PREFIX=$PREFIX \
'! ($1 in out) {out[$1] = (idx++ %NUM_FILES) } '\
'{ outfile= PREFIX"." out[$1] ".txt"; print | ("pigz >" outfile) ; next} '
すべてが期待どおりに機能しますが、メモリを使いすぎてコンピュータに空き容量が16GBしかないため、xGBより大きいファイルに分割することはできません。
この場合、どういうわけかメモリを無駄にすることができないのだろうか。
答え1
今週はユニコーンと虹を追うそうです。私は3つの戦略のスケジュールを開発しました。すべてのタイミングで私の2000万行をユーザーの(推定)80億行と推定していますが、タイミングは私のラップトップとハードドライブによって異なります。タイミングを合わせるためにテストファイルを圧縮しましたが、解凍にはgunzipのみを使用できます。
(A)行でいっぱいの読み出しメモリ(約10GB)をクリックしてからファイルにストリーミングします(一度に1つのファイル)。各IPを別のファイルに入れる必要があるという要件を誤解しました。これには、10 GB のデータごとに約 400,000 個のファイルを追加して閉じる作業が含まれます。私のランニングタイムは120時間。また、管理するファイルが400,000個残ります。これらのどれも良くありません。
(B) 36GBファイル多重処理(圧縮解除時360GB)。この時、私は分離すると500GBのデータ全体を保存するスペースが足りないと誤解しました。
実際、マルチパスコストはそれほど高価ではありません。アイデアは、IPをインデックスにグループ化することです。したがって、8回のパスを実行して、それぞれ100個のファイル、各ファイルに500個のIP、ファイルあたり約1000万行、800個のファイルを作成できます。実行時間は次のようにする必要があります。33時間、マルチパスはマルチファイルの追加よりはるかに高速です。他の利点もあります:
.. 次のグループを開始する前に、グループ内のすべてのファイルを圧縮できるため、ディスク容量が少なくなります。
.. たとえば、システム需要が低いときに夜間に実行するように各グループをスケジュールできます。
..各グループには、障害が発生したときに再起動ポイントがあります。
..グループファイルは、特定のIPを含むファイルを見つけるために使用されるインデックスです。
(C)あなたが言ったように、すべてのものを解凍したコピーを保存するのに十分なディスク容量があり、元のスクリプトも完璧に大丈夫です。、3つのマイナーな修正が行われた。
..プロセス内のすべての出力ファイルを圧縮することはできませんが、GNU並列処理を使用して注意してすべてのファイルに書き込んで後で圧縮できます。
.. awk出力ファイルの数から適切なタイミングを見つけることができます。経験的には、100個の出力ファイル(同じデータ量全体)は80個または120個の出力ファイルよりも高速です。 100を超える方法を分割しようとすると賢明ではない可能性があるため、各ファイルには約8000万のファイルラインと4000のIPを含む100のファイルがあります。
.. 各 IP に対してファイル名を 1 回書き込むのは、毎回書き込むよりもきれいです。
このアプローチの私の見積もりは次のとおりです。24時間。
これで、オリジナルは次のようになります。
time gunzip -c ../trial.data.gz | awk '
! ($1 in X) { X[$1] = sprintf ("Prefix_%.4d.dat", q++ % 100); }
{ print > X[$1]; }'
答え2
これらのタスクを実行する一般的なアプローチは、ワークロードを分割し、タスク全体を2段階で実行することです(ハードドライブはこの方法を好むでしょう)。
各IPアドレス(およびメモリに圧縮プロセスを持つパイプ)のファイルを生成する代わりに、最後のIPオクテットの各値のファイルを生成します。
unpigz -c $FILENAME | awk -v NUM_FILES=$NUM_FILES -v PREFIX=$PREFIX \
'{ last_octet=$0; sub("^[0-9]+\\.[0-9]+\\.[0-9]+\\.","",last_octet); }; '\
! (last_octet in out) {out[last_octet] = 1 } '\
'{ outfile= PREFIX"." last_octet ".txt"; print | ("pigz >" outfile) ; next} '
次に、各ファイルでコードを実行します。生成されたファイルが特定のサイズに達するまで(SSDに保存されていない場合)、メモリにアーカイブする方法を検討するのが合理的かもしれません。
答え3
これを行うためのGNU / awkスクリプトがあります。しかし、現時点で考慮すべきパフォーマンス、ディスク容量、およびファイル管理に関連するいくつかの問題があります。
デザインノート – 起源。
私のコードを見つけて、もう一度テストすると役に立つと思いました。これは簡単なフォーラムの投稿から始まりました。列に米国の州コードが含まれていることが知られているファイルがある場合は、どの州が存在するのかわからずにそれを別のファイルに分割する方法です。簡単です。データがいつ到着するかを通知します。新しい週ごとに新しいファイルを開きました。したがって、60以上のファイル(米国の海外領土を含む)に問題はありません。
私はGNU / awkがどこまで行くことができるか知りたいです。出力ファイルの数は無制限にサポートされていますが、ulimit(1024)に達すると閉じてから再度開いて偽にします。 ulimitを増やすことはできますが、おそらく400,000まで増やすことはできません。ファイル数が多く、ランダム入力シーケンスの場合、これは1行に1つのファイルを開閉するのと似ています(病理学的状況)。より迅速で簡単なソリューションを見つける前に、さまざまなキーフィールド(25Kのさまざまな値を含む)を実行し、マルチパス、カスケードツリー、およびプロセスツリーを使用してさまざまなデザインを試しました。
基本的な解決策は、できるだけ多くの行を取得して2D配列で埋めることです。次に、ファイルと元の順序で並べ替えて更新します。これはディスクの断片化を防ぎ、一度に1つの出力ファイルを開くだけです。
テストデータ。
nginx Webサイトのログ形式を調べた結果(すべてのオプションフィールドを設定しない限り)、IPアドレスの後に60文字のテキストがあるとします。そこで2千万行のデータ(1.5GB)を連結しました。
.. 400,000個のIPアドレスが拡張され、ランダムに指定されている: {10..29}.{30..39}.{80..129}.{110..149}
.. Harvard CS50テストパッケージから、40から90文字の長さ以外の行を除いて、490,306行のテキストを抽出します。
両方のデータは2000万行にコピーされ、接続されます。
評価する。
パフォーマンス - ノートパソコン、4GB、HDDで測定されました。
2000万行(1.5GB)が15分かかりました(圧縮されていないテキストで表示)。パフォーマンスがO(n)ではない理由はないので、480 GBの生データは約80時間かかります。あなたのサーバーはおそらく私のSamsung RV515よりはるかに速いでしょう。
メモリ
私のメモリ配列は、600万行と既知のIPおよびファイル名の増分配列を使用します。これは私の4MBのうち約2.7GBを使います。それより多いと、過剰なスワップとREISUBが発生します。
結果ファイル
より多くのスペースを使用すると便利です。更新するたびに、出力ファイルごとに平均15行(6M行/ 400Kキー)が追加されます。 10倍のメモリを使用すると、ファイル管理の90%を回避できます。
IPアドレスと同じくらい多くのファイルがあります。 1つのディレクトリに40万のファイルがあり、処理するのは非常に面倒です。
入力では、特定のIPの分布が重要な場合があります。私のデータはランダムに分散されており、これはおそらく最悪のシナリオです。使用パターンがIPにローカルにバースト的にアクセスし、再びアクセスしない場合、毎回フラッシュされるファイルはより少なくサイズが大きくなるため、より効率的です。
400,000個のファイル(一度に1つずつ)を追加できますが、ほとんどのコンテキストはプロセスに保存されているため、多くのプロセスを実行できないため、圧縮ファイルには追加できません。したがって、すべてのデータは一時的にプレーンテキストでなければならず、抽出後に個別に圧縮する必要があります。したがって、一時的に500GBの一時ディスク容量が必要です。
このコードを使用することができ、コードをカスタマイズするために必要な助けを得ることができます。ただし、データベースと適切なツールを使用する前に、このタイプのデータを分析する必要があります。