Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
OpenSearchでレガシーな検索処理の 大幅改善をやってやろう PHPカンファレンス小田原2025
Slide 2
Slide 2 text
自己紹介 ● 堂薗 伸樹(どうぞの のぶき) / DPon(@DPontaro) ○ 園 薗 ● 職業:エンジニア ● 所属:スターフェスティバル株式会社 ● 家族:妻 子👦👦 犬 ● ゲーム好き ● 大阪からきました ● 🎉 カンファレンス初登壇 🎉
Slide 3
Slide 3 text
会社とサービスの話 ● https://gochikuru.com/ ● 法人・団体向けの宅配弁当 ケータリング・オードブルの デリバリーサービス。
Slide 4
Slide 4 text
前段 プロジェクトの説明
Slide 5
Slide 5 text
プロジェクトの特徴 ● ごちクルとは別の特定業界向けのお弁当デリバリーサービス ● Initial Commitは2015年 ● コロナ禍で需要減。リソース都合もあり、しばらく専任のエンジニアがついていない 状態。 ● ときどき改善依頼があった際、手が空いてる人がスポットでアサインされる
Slide 6
Slide 6 text
パフォーマンスの課題 ● 2024年春、サイトのパフォーマンスは少し触れただけでも体感できるくらいに悪い ● 需要が戻りつつあるなか、マイナスの印象を与えるわけにもいかない
Slide 7
Slide 7 text
レガシーな環境
Slide 8
Slide 8 text
初手の改善
Slide 9
Slide 9 text
ボトルネックをみつける 商品検索のController。 リクエストされる頻度高く、かつ重い ページ。 中央値が 1.9 sec これに加え、ページ表示までに画像や cssのダウンロードなども発生する
Slide 10
Slide 10 text
初手の改善 スロークエリに手を入れてみる ● INDEXの追加 ● 該当箇所のコードを調整し、発行されるクエリを修正 結果200msec くらいは改善したが焼け石に水。
Slide 11
Slide 11 text
さてどうしたものか 長年専任がついていないプロジェクト、コードはツギハギで無駄も多い。 複雑に絡み合ったコードの解消のために、検索ロジック全体を組み直す必要がありそ う。 → それならもう OpenSearch に移行してもいいのでは?
Slide 12
Slide 12 text
OpenSearchとは ● 分散型の検索エンジン。Elasticsearchのフォーク版 ● クラスタ構成、複数のノードにデータ分散。 ● 高速な全文検索が可能
Slide 13
Slide 13 text
簡単に用語解説:Document ● Document:データを格納する単位。JSON形式。 ○ 学生のデータベースでは、 Documentは1人の学生を表せる ○ RDBにおける行に相当
Slide 14
Slide 14 text
簡単に用語解説:Index ● Index:Documentの集まり ○ RDBでいうテーブル ○ 学生のデータベースでは、 Indexはすべての学生を表す
Slide 15
Slide 15 text
簡単に用語解説:Clusterとか ● Cluster, Node ○ Clusterは、Nodeの集まり。 ○ Nodeはデータを保存、検索リクエストを処理 するサーバ。 ● Shard ○ いわゆる水平分割。 ○ Indexのdocumentを行単位でシャード分割し て分散 ○ シャードの分割目安:シャードのサイズを 10 ~ 50 GB に制限することです。(公式いわく)
Slide 16
Slide 16 text
OpenSearchへの移行
Slide 17
Slide 17 text
OpenSearch公式のPHPクライアント まぁバージョン下げたら対応してるやつあるやろ。 https://github.com/opensearch-project/opensearch-php
Slide 18
Slide 18 text
おっかしいなー
Slide 19
Slide 19 text
真面目にどうするか 理想はPHPのバージョンアップ。そうはいっても ● スポット的なアサインでもあり、メインは別プロジェクトの人員 ● 元のミッションはパフォーマンス改善。 →バージョンアップはスコープ外では? ● 認知負荷の高いコードの理解、スロークエリの対応など既にある程度時間もかけて いる。 →このタイミングからバージョンアップまでやる? ※言わずもがなテストはメンテされておらず
Slide 20
Slide 20 text
戦略的後回し // TODO PHPバージョンアップ 結局公式のPHP7.3が対応してるバージョンのクライアントを落としてから、7.1で動くよう にエラーが出る箇所をつぶして対応させました。 ※バージョンアップはパフォーマンス改善後に対応しました(7.4までですけど...
Slide 21
Slide 21 text
バッチの実装 毎時 RDS -> OpenSearch へデータ投入するバッチを実行。 その際”YYYYMMDD-HH”のサフィックスを持つINDEXが作成される。 例:product-index-20250412-15 INDEX作成後、アプリケーションが参照している OpenSearchのINDEXのエイリアスを変更。 実行前:product_index -> product-index-20250412-14 実行後:product_index -> product-index-20250412-15
Slide 22
Slide 22 text
マッピングの話 マッピングはOpenSearchに、ドキュメントとそのフィールドの保存方法とインデックスを指示します。 各フィールドのデータ型を指定することで (例えば、年を日付にするなど )、保存やクエリをより効率的に行うこと ができます。 Dynamic mapping (動的マッピング) 新しいデータやフィールドが自動的に追加される。 Explicit mapping (明示的マッピング) 推奨。正確な構造とデータ型を前もって定義できる。 パフォーマンスや正確性を高めることができる
Slide 23
Slide 23 text
マッピング一例 "id": map[string]string{"type": "integer"}, "product_name": map[string]interface{}{ "type": "text", "analyzer": "product_index_analyzer", "search_analyzer": "product_search_analyzer", },
Slide 24
Slide 24 text
analyze 文字列を検索に使えるように「分かち書き(トークン化)」&「正規化」する処理
Slide 25
Slide 25 text
analyzer と search_analyzer "product_name": map[string]interface{}{ "type": "text", "analyzer": "product_index_analyzer", "search_analyzer": "product_search_analyzer", }, analyzer:インデックス時(保存時)に使用する analyzer を指定。 search_analyzer:検索時に使用する analyzer を指定
Slide 26
Slide 26 text
synonym_filter search_analyzer には上記のsynonym_filterという同義語の設定がされている。 この設定により曖昧検索に対応できる "synonym_filter": map[string]interface{}{ "type": "synonym", "synonyms": []string{ "ウナギ,ウナジュウ", "子供,お子様",
Slide 27
Slide 27 text
やってみてどうだった
Slide 28
Slide 28 text
🎉結果発表🎉 before 中央値:1.9sec after 中央値:0.92sec
Slide 29
Slide 29 text
売上につながった? ここ1年で売上の数値はしっかり伸びている 🎉 複数要因があるものではありますが、パフォーマンス改善で下支えが出来た ※余談 社内の営業の方々から直接 DMでめちゃ助かったと何人か声かけていただいて、好感触
Slide 30
Slide 30 text
他改善するなら ● 毎時のバッチ実行 ○ 日々商品のデータは更新されているが、最大 1時間の遅延を許容している状態 ○ よりリアルタイムに更新したい場合、イベント駆動な仕組みを取り入れたり ● インフラのあいのり ○ ごちクルと同じインスタンスなので障害発生した際は、どちらも検索処理が機能しなくなる(今のとこ ろ発生はしていない) ○ 切り離せば懸念はなくなるが、コスト増にはなるのでバランス考えて ● synonym_filterなどがコード中に定義されている ○ 変更の際にエンジニアの手が入る状態となっているので、できれば外部ファイルに逃がしたほうが 良い