今回は当社のTRUE データフィードで使用している正規表現検索の効率化についての記事となります。
前提(背景と目的)
まずは宣伝(笑)当社公式サイトでは上記となっていますが、簡単にいうと
・クライアントからデータを預かる
・預かったデータを広告媒体毎のフォーマットの変換する
・変換したデータを指定された場所に送信する
ということをやっています。
その中のデータの変換時に禁止文言が入ったデータを行ごと除外するということをやっています。
大量のデータに禁止文言が入っているかをチェックする必要があるのですが、
初期想定では精々10万件に対して100~1000個の禁止文言をチェックする程度(1億回程度)で十分と判断していました。
ですが運用後に禁止文言が3万を超えてしまい、チェック処理が10万件×3万=30億回のチェックを行う場合があり、処理時間がかなり長くなってしまいました。
これが問題となり、上長とも相談してチェック処理の効率化を図ることになりました。
対処内容
対処方法はシンプルです。1件のチェック時に1個ずつ文言が含まれているかチェックしていたので、
1件のチェックで100個の文言を同時にチェックするようにしました。
具体的にはPHPのpreg_match、joinを使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
//関数群 //禁止文言をクウォートする $stocks = array(); $arrQuoteFunc = function($stocks){ return array_map( function($v){return preg_quote($v, '/'); } , $stocks ); }; //禁止文言が入った配列をjoinで「|」で繋いで1行にまとめる //その後データ1件に対して文言チェックを行う $checkMatchFunc = function($qstocks, $check_text){ return preg_match('/(' . join('|', $qstocks) . ')/' , $check_text); }; //実際の処理部分 //データ1件に対して禁止文言のチェックを行っている //従来の方法と異なり、禁止文言100個を1つに纏めて1回でチェックしている foreach($search_list as $search_word) { $stocks[] = $search_word; if (count($stocks) == 100){ $qstocks = $arrQuoteFunc($stocks); if ($checkMatchFunc($qstocks, $check_text)){ return true; } $stocks = array(); } } //禁止文言が1以上100未満の場合、ここで最後にチェックする if (count($stocks) > 0){ $qstocks = $arrQuoteFunc($stocks); if ($checkMatchFunc($qstocks, $check_text)){ return true; } $stocks = array(); } |
これにより従来の処理の100分の1の処理時間で済むようになりました。
やろうと思えば1000分の1なども出来ると思うが禁止文言の長さによってはPHPの制約に引っかかる可能性があるので100分の1までにしています。
おまけ(preg_matchの最大値)
pcre.backtrack_limit(PCRE のバックトラック処理の制限値)1000000バイト(PHP5.3.7以降)
100000バイト(PHP5.3.7以前)
pcre.recursion_limit(PCRE の再帰処理の制限値)
100000回
10万件以上のNGワードを登録されていたらエラーになっていましたね・・
現在は100倍まで問題ないので良かった。
まあPHPのメモリ使いすぎで落ちていたかもしれない。
まとめ
正規表現をまだまだ使いこなせていないな、と思いました。上長にこのやり方を教わって目から鱗が落ちる思いでした。
まだまだ改善や効率化できることがあると思うので尽力したいと思います。