IPごとにApacheアクセスログを解析したいです。次のコードを使用しましたが、ほぼ90秒かかりました。
grep "^$CLIENT_IP" /var/log/http/access.log > /tmp/access-$CLIENT_IP.log
その後、次の選択肢を試しました。
sed -i -e "/^$CLIENT_IP/w /tmp/access-$CLIENT_IP.log" -e '//d' /var/log/http/access.log
この作業も60秒以上かかりました。
解決すべきIPは1200個です。実行時間を短縮するために並列性を達成する方法があるかどうかを知りたいです。
答え1
おそらく、テキストファイルのIPアドレスを使用して、すべてのIPアドレスのシェルループでこれを行うとします。はい、IPアドレスsed
ごとに1回呼び出すとgrep
速度が遅くなります。
sed
代わりによく準備すれば一度は運が続くこともある。
まずスクリプトを作成する必要があります。sed
IPアドレスが1行に1つずつ含まれるファイルでこれを行います。ip.list
sed -e 'h' \
-e 's/\./\\./g' \
-e 's#.*#/^&[[:blank:]]/w /tmp/access-#' \
-e 'G' \
-e 's/\n//' \
-e 's/$/.log/' ip.list >ip.sed
これは、sed
各IPアドレスに対して
- アドレスを「予約済みスペース」(の追加バッファ
sed
)にコピーします。 .
「パターンスペース」(入力行)を\.
(ポイントを正しく一致させるためにコードではこれを行いません)に変更します。- パターンスペースの前に追加
^
します。[[:blank:]]/w /tmp/access-
- 改行文字を挟んで、予約済みスペースからパターンスペースに変更されていない入力行を追加します。
- 改行文字を削除します。
.log
行の末尾に追加し、結果を暗黙的に印刷します。
以下を含むファイルの場合
127.0.0.1
10.0.0.1
10.0.0.100
sed
これでスクリプトが生成されます。
/^127\.0\.0\.1[[:blank:]]/w /tmp/access-127.0.0.1.log
/^10\.0\.0\.1[[:blank:]]/w /tmp/access-10.0.0.1.log
/^10\.0\.0\.100[[:blank:]]/w /tmp/access-10.0.0.100.log
IPアドレスの後にスペース文字(スペースまたはタブ)が一致する必要があります。それ以外の場合は、ログエントリがファイル10.0.0.100
に保存されます/tmp/access-10.0.0.1.log
。あなたのコードはこれを無視します。
その後、繰り返しなしでログファイルで使用できます。
sed -n -f ip.sed /var/log/http/access.log
私は同じスクリプトでsed
1200個のファイルを書くことをテストしたことがありません。うまくいかない場合は、次のawk
バリエーションを試してください。
同様の回避策は、awk
まずIPアドレスを配列に読み込み、それを各行に一致させることです。これには一度のawk
呼び出しが必要です。
awk 'FNR == NR { list[$1] = 1; next }
$1 in list { name = $1 ".log"; print >>name; close name }' ip.list /var/log/http/access.log
ここではawk
IPリストとログファイルの両方を提供します。NR == FNR
まだ最初のファイル(リスト)を読んでいることがわかったら、IPlist
番号を連想配列のキーとして追加し、次の入力行に進みます。
条件が満たされない場合は、FNR == NR
2番目のファイル(ログファイル)から読み込み、入力行の最初のフィールドがキーフィールドであるかどうかをテストしますlist
(これは正規表現一致ではなく純粋な文字列比較です)。その場合は、適切な名前のファイルに行を追加します。
出力ファイルを閉じるときは注意が必要です。そうしないと、開いているファイル記述子が不足する可能性があります。したがって、追加するために開いたり閉じたりするファイルはたくさんありますが、awk
IPアドレスごとに1回呼び出す(またはその問題のユーティリティ)よりも高速です。
これがあなたにうまくいくか、おおよそのランタイムが何であるかを知りたいです。私は非常に小さなデータセットでのみこれらのソリューションをテストしました。
もちろん、私たちはあなたのアイデアに同意し、grep
システムに複数のインスタンスを並列に投げて、それを無差別に代入することができます。
IPアドレスのドットが正確に一致しなかったことを無視し、おそらく
xargs -P 4 -n 100 sh -c '
for n do
grep "^$n[[:blank:]]" /var/log/http/access.log >"/tmp/access-$n.log"
done' sh <ip.list
ここでは、短いシェルスクリプトにxargs
一度に最大100のIPアドレスがファイルから提供されます。ip.list
4つの並列スクリプト呼び出しをスケジュールします。
ショートシェルスクリプト:
for n do
grep "^$n[[:blank:]]" /var/log/http/access.log >"/tmp/access-$n.log"
done
xargs
これは、コマンドラインに提供されている100個のIPアドレスを繰り返し、grep
4つのループが並列に実行されることを除いて、使用したものとほぼ同じコマンドを適用します。
保持しているCPUの数を増やす-P 4
か、それに関連しています。各並列インスタンスが同じディスクから読み書きできる-P 16
ため、スピードアップは線形ではない可能性があります。grep
-P
この回答のすべての内容は、タグを除くxargs
すべてのPOSIXシステムで実行できる必要があります。-P
フラグはxargs
非標準ですが、xargs
GNUシステムとBSDシステムで実装されています。
答え2
さまざまな方法の場合: https://stackoverflow.com/questions/9066609/fastest-possible-grep
それ以外にも、これを頻繁に実行する場合は、SSDが最善の選択です。 HDへのアクセスは、この種の仕事のためのキラーです。
実行するさまざまなgrepがあります。スクリプトコマンド(コアごとに1つ)をバックグラウンドで実行するスクリプトを作成し、完了すると、より多くのコマンドの実行が完了した時点を追跡します。
これにより、12コアすべてを100%CPU使用率で実行できますが、リソース制限は他の問題になる可能性があります。すべての操作に同じファイルが必要であることを考慮すると、SSDを使用していない場合はファイルを共有しないようにコピーすることをお勧めします。
答え3
RAMよりも大きく、キャッシュできない場合は、より多くのプロセスを並列に実行することが、複数の読み取りに対する良い選択肢となる可能性が/var/log/http/access.log
あります。特にコアが複数ある場合はさらにそうです。access.log
これにより、grep
IPごとに1つ(+複数のヘルパーラッパープロセス)が並列に実行されます。
pargrep() {
# Send standard input to grep with different match strings in parallel
# This command would be enough if you only have 250 match strings
parallel --pipe --tee grep ^{} '>' /tmp/access-{}.log ::: "$@"
}
export -f pargrep
# Standard input is tee'ed to several pargreps.
# Each pargrep gets 250 match strings and thus starts 250 processes.
# For 1200 ips this starts 3600 processes taking around 1 GB RAM,
# but it reads access.log only once
cat /var/log/http/access.log |
parallel --pipe --tee -N250 pargrep {} :::: ips