grep検索に基づいてスクリプトを高速化する方法は?

grep検索に基づいてスクリプトを高速化する方法は?

2つのカンマ区切り値を含む非常に大きなテキストファイルがあります。

78414387,10033
78769989,12668
78771319,13677
78771340,13759
80367563,16336
81634533,10025
82878571,10196
110059366,10218
110059411,10812
110059451,10067

次のようにログファイルからその値を取得する必要があります。

- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c737e3d80d7def296c7| id=278832702| version=28| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c732f18c26fe604fd04| id=284057302| version=9| timestamp=1467432051000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c747e3d80d7def296c8| id=357229| version=1151| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c742f18c26fe604fd05| id=279832706| version=35| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:52 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c744697ddb976cf5a95| id=354171| version=503| timestamp=1467432052000
- delivery-AMC_prod_product 231825855936862016-07-02 00:00:53 c.c.i.d.s.d.DeliveryTopologyFactory$$anon$1$$anon$2 [INFO] ack: uid=57773c754697ddb976cf5a96| id=355638| version=1287| timestamp=1467432053000

私のスクリプト:

#!/bin/bash
COUNT=0
while IFS=',' read ID VERSION; do
    VERSION=`echo $VERSION |col -bx`
    if (grep "id=${ID}| version=$VERSION" worker-6715.log.2016-$1.log.* > /dev/null); then
            let "COUNT++"
    else
            echo "$ID, $VERSION FAIL"
            exit 2

    fi
done < recon.txt

echo "All OK, $COUNT checked"
  • ログファイルから不要なフィールドを削除すると実行速度が速くなりますか?
  • RAMデバイスを作成してそこにログファイルをコピーすると、実行速度が速くなりますか?Red Hat Linux 6(Hedwig) ファイルをキャッシュする方法は?他に提案がありますか?

答え1

ボトルネックは、recon.txtシェルからファイルを1行ずつ読み取っています。失敗した行を取得するには、ログ内の行を前処理して行のように見えるようにし、それを使用してrecon.txtcomm(1)のように設定の違いを見つけることができます。

comm -23 \
    <(sort -u recon.txt) \
    <(sed 's/.*| id=\([0-9]*\)| version=\([0-9]*\)|.*/\1,\2/' worker-6715.log.2016-$1.log.* | \
        sort -u)

これは、構成を処理できるシェルがあると仮定します<(...)。また、結果の行の順序は維持されませんrecon.txt。この順序を維持することはより困難で遅いでしょう。

成功回数も必要な場合は、反対の操作を実行してrecon.txtログで見つかったのと同様に見えるように前処理してから、またはをfgrep(1)使用grep -Fして検索できます。ロケールを次に設定すると、C一部のシステムで作業速度が大幅に向上する可能性があります。したがって:

COUNT=$( \
    sed 's/\([0-9]*\),\([0-9]*\)/| id=\1| version=\2|/' recon.txt | \
    LC_ALL=C fgrep -f - worker-6715.log.2016-$1.log.* | \
    wc -l )

これはrecon.txt、重複エントリを含まず、各行がすべてのrecon.txtログで最大1回一致することを前提としています。最初の制限を解除するのは難しいでしょう。 2番目は慎重な選択で持ち上げることができますcomm(1)

答え2

指摘したように、ここで主な問題はシェルループです。まずawk、ログファイル、接続、およびidversionを処理し、結果を配列に保存してから読み取り、各行が配列recon.txtにあることを確認します。それ以外の場合は、行の内容を変数に保存してtすぐにexitENDブロックを実行)。ブロックにENDメッセージと共に保存された行がある場合、それ以外の場合はOKメッセージのみを印刷し、何も印刷しません。行数:texit 2recon.txt

awk 'NR==FNR{j=$9","$10;gsub(/[^0-9,]/, "", j);a[j]++;next}
!($0 in a){t=$0;{exit}}
END{if (t){print t, "FAIL"; exit 2}
else{print "All OK,", FNR, "checked"}}' logfile recon.txt

idこれは、値がversionそれぞれフィールド 9 と 10 にあると仮定します。それ以外の場合は、次の正規表現を使用できます。佐藤実際には - つまり、awkスタイルが逆参照をサポートしている場合、そうでなければ次のことが起こります。

NR==FNR{sub(/.* id=/, "");sub(/\| version=/, ",");sub(/\|.*/, ""));a[$0]++;next}

答え3

私はこれらのファイルをデータベース(必要なすべてのタイプ)に入れます。おそらく、大きなファイルをインポートせずにその出力を生成したアプリケーションから1行ずつ入力することもできます。

これにより、DBエンジンは毎回grepを呼び出してリソースを無駄にせず、代わりにCでコンパイルされた内部関数を使用します。

インポート(ファイル全体をインポートする場合)またはテーブルを作成した後(行ごとにレコードを挿入する場合)、インデックスを作成することを忘れないでください。

答え4

まず、recon.txtをgrep -Fがすばやく使用できる形式に変換します。

perl -pe 's/,/| version=/' recon.txt > recon.grep-F

もしそうなら、あなたの状況は上記のようにhttps://www.gnu.org/software/parallel/parallel_examples.html#example-grepping-n-lines-for-m-regular-expressions

あなたの質問に合ったテキストは次のとおりです。

正規表現の多い大容量ファイルをgrepする最も簡単な解決策は次のとおりです。

grep -f regexps.txt bigfile

または正規表現が固定文字列の場合:

grep -F -f recon.grep-F worker.log

CPUとディスクI / Oという2つの制限要因があります。 CPUは測定が簡単です。 grepがCPUの90%を超える場合(例:top実行時)、CPUは制限要因であるため、並列化がスピードアップします。そうでない場合、ディスクI / Oは制限要因であり、ディスクシステムによっては並列化が速くなったり遅くなったりする可能性があります。確かに知っている唯一の方法は測定することです。

CPUが制限要素の場合は、正規表現を並列化する必要があります。

cat recon.grep-F | parallel --pipe -L1000 --round-robin grep -F -f - worker.log

複数の正規表現と一致すると、1行が重複する可能性があります。このコマンドはCPUごとにgrepを起動し、CPUごとに1回大きなファイルを読み取ります。ただし、これは並行して実行されるため、最初の読み取り以外のすべての読み取りはRAMにキャッシュされます。 regexp.txtのサイズによっては、-L1000の代わりに--block 10mを使用する方が速いかもしれません。 regexp.txtが大きすぎてRAMに収まらない場合は、--round-robinを削除して-L1000を調整してください。これにより、ビッグファイルをもっと読むことができます。

一部のストレージシステムは、複数のブロックを並列に読み取る場合にパフォーマンスが向上します。これは、いくつかのRAIDシステムといくつかのネットワークファイルシステムに当てはまります。大容量ファイルを並列に読み取る:

parallel --pipepart --block 100M -a worker.log -k grep -F -f recon.grep-F

これにより、ビッグファイルが100 MBのチャンクに分割され、各チャンクに対してgrepが実行されます。 bigfile と regexp.txt を並列に読み取るには、--fifo を使って 2 つを結合します。

parallel --pipepart --block 100M -a worker.log --fifo cat recon.grep-F \
\| parallel --pipe -L1000 --round-robin grep -f - {}

複数の正規表現と一致すると、1行が重複する可能性があります。

関連情報