PostgreSQL(現在は9.6ですが、アップグレードは可能です)を使用して、私は現在、次のデータベースレイアウトで顧客が製品を注文でき、それ自体がカテゴリに分類されています(製品は複数のカテゴリにある場合があります)。
Orders
id -- PRIMARY KEY
customer_id -- FOREIGN KEY (Customer - id)
product_id -> FOREIGN KEY (Product - id)
Products
id -- PRIMARY KEY
Categories
id -- PRIMARY KEY
Product_Categories
product_id -- FOREIGN KEY (Product - id)
category_id -- FOREIGN KEY (Category - id)
現在、私はかなり大量の注文(〜3千万)と適切な数のカテゴリ(〜1K)と顧客(〜1万)を持っています。約3万の製品があり、カテゴリごとに平均3つの製品があります。製品はカテゴリから別のカテゴリに移動されることがあります(月に1回シャッフルとしましょう)
私の問題は、次のタイプのクエリを高速に実行させたいことです"カテゴリCにある製品の顧客のすべての注文を取得します"。それは次のようになります:
SELECT * FROM Orders
JOIN Product_Categories ON Orders.product_id = Product_Categories.product_id
WHERE Orders.customer_id = X AND Product_Categories.category_id = Y
私が考えることができる最良のインデックスは、Ordersのcustomer_id
のインデックスであり、Product_Categories.product_id
のセカンダリインデックスによってサポートされています。これは次の計画につながります(上で示した設計は実際のケースを非常に単純化しているため、実際の計画ではありません)。
- Index Scan on Orders using index on customer_id ---> Returns ~10K Rows
- 10K Joins done by Index Lookup on the product_id index of Product_Categories (MAIN TIME CONSUMER)
- 9990 Rows Filtered Out.
- 10 Rows Returned
(customer_id, category_id)
にインデックスを付けたいのですが、これを行う方法を見つけることができませんでした。私が考えることができる最良の解決策は、列categories_id INTEGER[]
を追加してから、次のいずれかを行うことです:
categories_id
およびcustomer_id
を使用し、リスト演算子に含めてGINインデックスを追加します。order_id
に1000個の部分インデックスを作成します。どちらの場合でも、categories_id
をcategory
↔product
関連付けテーブルの更新と同期する必要がありますが、これは残念です。
私の質問は:
インデックスon product_categories (category_id)
があり、すでにon orders (customer_id)
があるインデックスがある場合、このタイプのクエリは非常に高速です。各テーブルで非常に具体的なインデックススキャンを個別に実行し、結果をハッシュ結合できます。
https://explain.depesz.com/s/JEpZ
それが十分に速くない場合、またはインデックスが配置されている場合でもそのようなプランを使用することができない場合は、実際のクエリのように、より多くの情報を提供する必要があると思いますタイミングを含めた計画、および達成したい時間。