Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPで巨大データ検索の高速化: strposと計算量の重要性

PHPで巨大データ検索の高速化: strposと計算量の重要性

2025/03/23 PHPerKaigi2025 LT大会
PHPで巨大データ検索の高速化:strposと計算量の重要性

PHPで大量データを扱う際、コードが正しく動いていても、パフォーマンスの低下が発生し、ユーザーの離脱につながったりすることがあります。
本トークでは、私たちのプロジェクトで直面した「strposによる文字列検索が原因で処理が30秒以上かかった」事例をもとに、計算量の重要性とパフォーマンス最適化についてお話しします。

問題の背景は、数千件の文字列を1件ずつ、約1万件の集合と照合する処理でした。
文字列の部分一致のチェックをstrposで実装していましたが、線形検索(O(n))のため、1回の検索がミリ秒単位でも、繰り返し処理が積み重なり、最終的には数十秒に達しました。
処理の遅延が原因でタイムアウトが発生し、ユーザーが機能を使えない危機に直面しました。

解決策として、array_flipとarray_key_existsを用いて、検索処理をハッシュベース(O(1))に変更することで、処理時間を30秒から約6秒に短縮しました。
この経験を通じて、パフォーマンス問題を解決するには計算量の意識が不可欠であると学びました。

このように、1回の処理がわずか1ミリ秒遅延しただけであっても、大規模データでは致命的な影響を与える可能性があること、計算量や計算時間が特に大事になってくることを学びました。
このようなパフォーマンス改善は、ユーザー体験を向上させ、機能の価値を最大化するための重要なステップです。
ユーザーが快適に使える環境を提供するため、私たちエンジニアが取り組むべき課題を一緒に考えてみませんか?

https://fortee.jp/phperkaigi-2025/proposal/1740b466-cd6f-4895-8362-2f16c8ace62e

Masaki Yokoyama

March 25, 2025
Tweet

More Decks by Masaki Yokoyama

Other Decks in Programming

Transcript

  1. まさき。
 NE株式会社 (開発者兼プロダクトオーナー)
 小田原を捨てて(ないよー)横浜市民に!
 
 その他
 - 引っ越し時はサブスクや届くはずの郵便物が遅延してないか気にしま しょう
 -

    ノベルティBOXが小田原に配達されてしまって受け取ったのがそれから1 週間後
 - 転送料金がかかる。4つくらい払って4000円くらいの出費、、、 
 - アニメDr.STONEに今更ながらハマり、最新話に追いつきました 
 6
  2. 検索スコアリングのアルゴリズム
 16 <?php for (テキストのリストでループ ) { for (検索キーワードのリストでループ )

    { if (strpos(検索キーワード , テキスト) !== false) { //テキストで見つかったキーワードの数をカウント } } } // もっともキーワードの数が多かったテキストを採用
  3. ボトルネックの特定
 ログで各処理の実行時間を計測し原因を特定
 20 <?php $start = microtime(true); if (strpos(検索キーワード ,

    テキスト ) !== false) { //テキストで見つかったキーワードの数をカウント } $end = microtime(true); logger()->debug("Execution time: " . ($end - $start) . " sec");
  4. 解決策2 線形探索O(N)をハッシュO(1)に!
 
 25 $texts = [ // strpos エスティーアールポス

    読み方 日本語 [ 'strpos' => 0, 'エスティーアールーポス' => 1, '読み方' => 2, '日本語' => 3, ], // str_contains 使い方 [ 'str_contains' => 0, '使い方' => 1, ], ]; 検索される側が連想配列のキーに
 キーワードを持っていれば、
 1発でそのキーワードがそのテキストに
 含まれるかがわかる

  5. 解決策2 線形探索O(N)をハッシュO(1)に
 26 <?php // テキストに含まれる単語をキーに持つ連想配列のリストを作っておく for (連想配列のリストでループ ) {

    for (検索キーワードのリストでループ ) { // 1発でそのテキストにそのキーワードが含まれているかがわかるようになった O(1) if (isset(連想配列[検索キーワード ])) { //テキストで見つかったキーワードの数をカウント }  } } // もっともキーワードの数が多かったテキストを採用
  6. まとめ
 O(N)の処理を膨大に繰り返した結果、パフォーマンスが破綻。
 ✅ 仕様を見直して繰り返し回数を削減
 ✅ ハッシュでO(1)に最適化
 
 処理の計測 → ボトルネックの特定→

    計算量削減 の流れが重要。
 小さな遅延も積み重なればUXを損なうと実感。
 
 もっと良い方法があればぜひ教えてください!
 29