Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

ElasticsearchでECサイトにおける高速検索/集計を実現する

樋口慎
October 14, 2022

 ElasticsearchでECサイトにおける高速検索/集計を実現する

Elastic Community Conference 2022 の発表資料です。
https://community-conference.elastic.co/session/309670

セッション概要
===========================================
ECサイト検索における性能改善についてのセッションです。

ECサイトは商品の属性(色や重量、内容量など)によって、件数を集計して表示したり、チェックボックスによってフィルタリングをおこなったりする機能を有するものが一般的です。

Elasticsearchでそういった集計・フィルタリングを実現するための商品データを扱うために各商品の属性を個別のフィールドとして扱うと、属性値のバリエーション数が膨大になります。 これは、商品の種類によって保持している属性値の種類が異なるためです。

フィールド数が膨大になるとヒープ利用効率が悪くなるため、ヒープが逼迫しクエリ時にGCが発生しやすくなる場合があります。これによって検索性能が出なくなります。 また、ドキュメント登録時には頻繁に mapping が更新されるため、インデクシングのパフォーマンスを低下させる恐れもあります。

上記の課題を解決し、高速な検索・集計をElasticsearchで実現する方法やTipsについてお話します。

樋口慎

October 14, 2022
Tweet

More Decks by 樋口慎

Other Decks in Technology

Transcript

  1. 自己紹介 • アクロクエストテクノロジー株式会社 • 樋口 慎(@shin0higuchi) • 業務:Elasticコンサルティング全般、データ分析、システム開発 • その他

    ◦ Elastic Certified Professional (世界初 Elastic Certification 3種取得) ◦ Elasticsearch NEXT STEP 執筆 ◦ Elastic User Group Tokyo 運営
  2. アクロクエストテクノロジー株式会社 • オープンな社風が特徴のITベンチャー ◦ 働きがいのある会社ランキング日本1位(3回受賞) ◦ 『日本でいちばん大切にしたい会社』大賞 審査委員会特別賞 ◦ 組織コンサルティング事業を展開

    • データ活用ビジネス ◦ Elastic Stack活用コンサルティング ✓ 設計/構築/性能改善/運用支援など ✓ Elastic認定資格者:8名 ◦ IoTデータ分析プラットフォーム ◦ 機械学習/AI
  3. ECサイト検索の機能要件 • 一般的にECサイト検索に求められる機能要件 親子関係を 含む データ構造 ファセット 集計 レコメンド/ サジェスト

    フィルタリング 商品検索 商品グループ SKU ※1 価格 レビュー数 商品属性 全文検索 表記ゆれ対応 類義語対応 商品属性絞込み 範囲検索 もしかして検索 クエリ補完 パーソナライズ ※1:Stock Keeping Unit ⇒ 管理・販売の最小単位
  4. ECサイト検索の機能要件 • 一般的にECサイト検索に求められる機能要件 親子関係を 含む データ構造 ファセット 集計 レコメンド/ サジェスト

    フィルタリング 商品検索 商品グループ SKU ※1 価格 レビュー数 商品属性 全文検索 表記ゆれ対応 類義語対応 商品属性絞込み 範囲検索 もしかして検索 クエリ補完 パーソナライズ ※1:Stock Keeping Unit ⇒ 管理・販売の最小単位
  5. ECサイト検索の機能要件と画面イメージ 軍手 軍手A 白 \1,000 レビュー □ 4以上 ☑ 3以上

    □ 2以上 □ 1以上 軍手B 厚手 \1,500 手袋 丈夫 ゴム製 \2,000 ブランド □ XX (100) □ YY (57) □ ZZ (12) ・ ・ ・ 40件の商品があります 商品検索 フィルタリング データの親子関係 検索結果画面に何を表示するかは、サイトによって異なる。 例)結果画面には商品Gのみ、詳細画面でSKU一覧を表示 例)結果画面にSKUを一覧表示 ファセット集計
  6. ECサイト検索の特徴 • データ特性 ◦ 検索対象となる商品グループ/SKUの数が多い。 商品グループで1000万、SKU単位で2000万を超える事例もある。 ◦ 商品属性の種類が多い。 (例:色、サイズ、etc.) 扱う商品が多様な場合、数万以上となる。

    • 性能要件 ◦ 単発クエリの検索性能要件(100ms以下~1000ms以下) クエリ内のファセット集計の数が多い。集計性能のチューニングも重要。 ◦ 同時検索数の性能要件も存在する。(同時10人~500人)
  7. Flattened Field Type によるアプローチ • Elasticsearch7.3から導入されたFlattened field typeを利用することで、 フィールド数の増大を解決することができる。 参考)https://www.elastic.co/guide/en/elasticsearch/reference/current/flattened.html

    • Flattened field type ◦ オブジェクトの子フィールドを、まとめて1つのフィールドとして扱うデータ型 ◦ 子フィールドのユニーク数が膨大 or 数が不明な場合に有用 { "object": { "xxx": "aaa", "yyy": "bbb", "zzz": "ccc" } } オブジェクトの例 ①Flattenedを利用しない場合、 ・object.xxx ・object.yyy ・object.zzz の3フィールドが作られる。 ②Flattenedを利用する場合、 object という1フィールドのみが作られる。
  8. Flattened Field Type の特徴 • 以下のようにオブジェクトを単一のフィールドとして扱えるため、 フィールド数を大きく減らすことが可能 • クエリ時には "attributes.length"

    のような記述で子階層にアクセス可能 { "length": "200mm", "color": "青", "weight": "500g", "width" : "10mm", ・・・ } { "attributes": { "length": "200m", "color": "青", "weight": "500g", "width" : "10mm", ・・・ } } Flattened 未使用 Flattened 使用 ・属性の数だけフィールドが必要 ・数万フィールドにおよぶ場合も "attributes" という単一のフィールド として扱うことが可能
  9. 実際の利用例 • 商品属性名の重複を考慮し、flattened配下のフィールド名は 名称ではなくID化する。 { "attributes": { "00001": "200mm", "00002":

    "青", "00003": "500g", "00004" : "10mm", ・・・ } } 属性IDと属性名称の対応は、 別フィールドや、Elasticsearch外部に持たせることが多い (Elasticsearchには検索に必要な情報のみ保持) 00001 → 長さ: 200mm 00002 → 色 : 青 00003 → 重さ:500g 00004 → 太さ:10mm
  10. • Terms Aggsで集計 集計時のクエリ例 "aggs": { "attributes_00001":{ "terms": { "field":

    "attributes.00001" } }, "attributes_00002":{ "terms": { "field": "attributes.00002" } } } 軍手 軍手A 白 \1,000 長さ □ 200mm (30) □ 100mm (50) □ 80mm (25) □ 50mm (120) 軍手B 厚手 \1,500 手袋 丈夫 ゴム製 \2,000 色 □ 白 (100) □ 黒 (57) □ 灰 (12) ・ ・ ・ 40件の商品があります 00002 → 色 00001 → 長さ
  11. 基本的なデータ構造 商品グループA SKU1 SKU2 SKU3 商品グループB 商品名 カテゴリ ブランド 商品概要

    商品属性 価格 … 色:白、サイズ:A4、価格:\300 色:青、サイズ:B5、価格:\240 長さ:120mm、入数:10、価格:\500
  12. 基本的なデータ構造(RDBの場合) 商品GID 商品G名 カテゴリ … 00001 商品A 日用品 00002 商品B

    日用品 00003 商品C 工具 SKU ID 商品GID(FK) 価格 サイズ 長さ 00001 00001 300 A4 - 00002 00001 240 B5 - 00003 00002 500 - 100mm 商品グループテーブル SKUテーブル 検索時には、 2つのテーブルをJOINする
  13. 基本的なデータ構造(Elasticsearchの場合) • Elasticsearchでは、複数テーブル(インデックス)のJOINはサポートされていない • JOINに相当するデータ設計として以下の3パターンがある。 No. データ構造 ドキュメント単位 親子関係の表現方法 1

    SKUに非正規化 SKU 全ドキュメントが、 商品グループ(親)の情報を持つ 2 Nested Field Type 商品グループ 配列でSKUのオブジェクトを持つ (Nested型) 3 Parent Child 商品グループ / SKU Join Field Typeで親子関係を表現
  14. No.1: SKUに非正規化 ドキュメント単位:SKU {"product_group_id": "00001", "product_name": "商品A", "sku_id": "00001", "price":

    300, "color": "白", "サイズ": "A4"} {"product_group_id": "00001", "product_name": "商品A", "sku_id": "00002", "price": 240, "color": "黒", "サイズ": "B5"} 商品GID 商品G名 カテゴリ 00001 商品A 日用品 SKU ID 価格 色 サイズ 00002 240 黒 B5 SKU ID 価格 色 サイズ 00001 300 白 A4 商品グループ SKU 商品グループ情報は各ドキュメントに重複して記載
  15. No.1: SKUに非正規化 ドキュメント単位:SKU {"product_group_id": "00001", "product_name": "商品A", "sku_id": "00001", "price":

    300, "color": "白", "サイズ": "A4"} {"product_group_id": "00001", "product_name": "商品A", "sku_id": "00002", "price": 300, "color": "黒", "サイズ": "B5"} … • メリット ◦ クエリがシンプルに記述でき、検索が高速である ◦ SKU同士のクロスヒットを回避できる • デメリット ◦ 商品グループの集計が近似計算(不正確)になってしまう ◦ 商品グループの更新をする場合、紐づく全てのSKU更新が必要。(更新が遅い)
  16. No.2: Nested Field Type ドキュメント単位:商品グループ 商品GID 商品G名 カテゴリ 00001 商品A

    日用品 SKU ID 価格 色 サイズ 00002 240 黒 B5 SKU ID 価格 色 サイズ 00001 300 白 A4 商品グループ SKU { "product_group_id": "00001", "product_name": "商品A", "sku": [ {"sku_id": "00001", "price": 300, "color": "白", "サイズ": "A4"}, {"sku_id": "00002", "price": 300, "color": "黒", "サイズ": "B5"}, … ] }
  17. No.2: Nested Field Type ドキュメント単位:商品グループ { "product_group_id": "00001", "product_name": "商品A",

    "sku": [ {"sku_id": "00001", "price": 300, "color": "白", "サイズ": "A4"}, {"sku_id": "00002", "price": 300, "color": "黒", "サイズ": "B5"}, … ] } skuフィールドのデータ型を "nested" にする • メリット ◦ SKU同士のクロスヒットを回避できる ◦ 商品グループの集計が正確 • デメリット ◦ クエリが複雑になる/非正規化アプローチと比較して検索速度が遅い ◦ SKUを更新時、更新対象SKU以外のフィールドの再登録が必要。(更新が遅い)
  18. No.3: Parent-Child ドキュメント単位:商品グループ/SKU 商品GID 商品G名 カテゴリ 00001 商品A 日用品 SKU

    ID 価格 色 サイズ 00002 240 黒 B5 SKU ID 価格 色 サイズ 00001 300 白 A4 商品グループ SKU { "id": "product_00001", "product_name": "商品A", "relation": "product_group"} … {"id": "sku_00001", "price": 300, "color": "白", "サイズ": "A4", "relation": {"name":"sku", "parent": "product_00001"}} {"id": "sku_00002", "price": 300, "color": "黒", "サイズ": "B5", "relation": {"name":"sku", "parent": "product_00001"}} …
  19. No.3: Parent-Child ドキュメント単位:商品グループ/SKU { "id": "product_00001", "product_name": "商品A", "relation": "product_group"}

    … {"id": "sku_00001", "price": 300, "color": "白", "サイズ": "A4", "relation": {"name":"sku", "parent": "product_00001"}} {"id": "sku_00002", "price": 300, "color": "黒", "サイズ": "B5", "relation": {"name":"sku", "parent": "product_00001"}} … relationフィールドをjoin型にして、ドキュメント種別(商品 or SKU)や親のIDを保持する • メリット ◦ SKU同士のクロスヒットを回避できる ◦ 商品グループの集計が正確 ◦ SKU単体でのデータ更新が可能。(Nestedより更新が高速) • デメリット ◦ クエリが複雑になる ◦ Nestedよりさらに検索速度が遅い
  20. Elasticsearchでのデータ構造まとめ 各データ構造に適した検索要件/特徴 No. データ構造 適した 検索要件 クエリ/ 検索速度 クロスヒット 回避

    商品グループの 集計 データ更新 1 SKUに非正規化 SKU主体 シンプル/高速 可能 近似 遅い 2 Nested Field Type 商品グループ主体 複雑/遅い 可能 正確 遅い 3 Parent Child 商品グループ主体 複雑/とても遅い 可能 正確 速い • SKU主体の要件の場合、「SKUに非正規化」を採用する。 ただし、商品グループの集計が必須要件の場合は不向きである。 • 商品グループ主体の要件の場合、NestedまたはParent Childを採用する。 • データ更新の頻度/量が 少ない場合→検索がParent Childより高速なNestedを採用する 多い場合→検索は遅いがデータ更新が高速なParent Childを採用する