【Perl】Parallel::ForkManagerモジュールで並列処理が便利過ぎて生きているのが辛い
とあるDBを利用したバッチ処理をやらせようとした時に
#スクリプトの一部分 my @list1 = (適当なリストその1); my @list2 = (適当なリストその2); foreach my $value1 (@list1){ foreach my $value2(@list2){ #DBに接続して、あるテーブルのレコード件数countする→ #その結果を別テーブルにUPDATEするSQL実行処理; } }
というまあ、foreachでグルグルグルグル回すスクリプトを書いたんですね。
で、まぁこれがクソ重かったんですね。テーブルのデータは全部メモリ上に載っているんですが、
10GB(1億レコードぐらい)ぐらいのテーブルで順番に$value1,$value2の変数を条件に使ってCOUNT()を使用した
SELECT文を投げるので、一つのSQL投げたらDB側のCPUコアが100%になってしまって処理完了までに数十秒掛かると。
で、ループの回数が全部で10回とかなら良いんですが、5000回は回るんですよね。
そうすると、一発動かしてから完了させるのに4〜5時間とか掛かってしまう。
で、同じような事をさらに今後は数種類やりたかったんですよ。でも、この処理時間見ると
「いまどき無いわー。やっぱHadoop使えないと生き残れないの?俺死ぬの?」ってなって凹むわけですよ。
ただ、DBサーバの状況を見るとそこそこ良いサーバなので論理コア16コアあるんですが、残りの15コアは使用率低いんですよ。
ん?(・ω・)という事は
・一つのSQLはクソ重い。(一発で数十秒掛かる。ただ、ちゃんとしたテーブルのチューニング出来たら早いかもw)
・でも同時に並列実行出来ればその分処理時間短縮されるから、少しは現実的になるよね。
・それぞれのSELECT→UPDATEは範囲がかぶらないので同時実行しても問題無し。
という条件なので残りのコアも有効利用したら、データはメモリに載っかっているんだから
DiskI/O発生しないので、同時実行分単純に早くなるんじゃね?
という事で、SQL処理部分を同時実行するように書き換えてみようとしました。
で、ググって見た所普通にfork()やろうとするとなんか良く解らんのですよ、ぼくバカだから(^p^)
ここらへんとか見ても、同時にforkさせる数をサーバによって変えたかったんだけど、使い方良く解らん。
fork プロセスを分岐する http://d.hatena.ne.jp/perlcodesample/20090413/1240326405
「こんな基本的な事も良く分からないの?バカなの?死ぬの?」ってまた勝手に凹むわけですね。
で、数分見て面倒くさくなって、もっと楽な方法ねーかなーって調べたら
Parallel::ForkManager 使って並行ダウンローダ作った
というサンプル付きで解説頂いてますが、素晴らしく楽にfork出来るCPANモジュールがあるんじゃないですか。
で、詳しい内容はリファレンス見てもらうとして、以下の様に書き換えました。
use Parallel::ForkManager; #並列実行をとりあえず8で my $pm = Parallel::ForkManager->new(8); my @list1 = (適当なリストその1); my @list2 = (適当なリストその2); foreach my $value (@list1){ foreach my $value2(@list2){ $pm->start and next; #DBに接続して、あるテーブルのレコード件数countする→ #その結果を別テーブルにUPDATEするSQL実行処理; $pm->finish; } $pm->wait_all_children; }
という感じでスクリプト書き換え実行して見た所、MySQLサーバの様子を見たら予想通り
・さっきまで1コアしか頑張っていなかったけど、8コアたっぷり100%に頑張っている。
・一つ一つの処理時間は前と変わらない。(多分、DiskI/O発生したら余計遅くなるとは思う)
・なので単純に全体の計算時間は1/8程度に短縮。
という現実的な処理時間に収める事が出来ました。
子沢山は素敵ですね!!