3

出荷(200Kレコード)、店舗(45レコード)、product_stores(8Kレコード)、地区(698レコード)の4つのテーブルがあります。次のクエリは、実行に非常に長い時間(12秒)かかります。

SELECT `s`. * , `p`.`productCode` , `p`.`productName` , `st`.`name` AS `storeName` , `d`.`name` AS `districtName`
FROM `shipments` AS `s`
JOIN `product_stores` AS `p` ON s.productStoreId = p.id
JOIN `stores` AS `st` ON s.storeId = st.id
LEFT JOIN `districts` AS `d` ON s.districtId = d.id
WHERE (s.storeId IN (1, 2, 3, 4, 6, 9, 14, 16, 22, 26, 30))
GROUP BY `s`.`id`
ORDER BY `s`.`id` DESC
LIMIT 100

EXPLAINクエリは、次の結果を返します。

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  st  ALL     PRIMARY     NULL    NULL    NULL    45  Using where; Using temporary; Using filesort
1   SIMPLE  s   ref     fk_shipments_stores1_idx,fk_shipments_product_stor...   fk_shipments_stores1_idx    4   st.id   482     
1   SIMPLE  p   eq_ref  PRIMARY     PRIMARY     4   s.productStoreId    1   
1   SIMPLE  d   eq_ref  PRIMARY     PRIMARY     4   s.districtId    1   
  1. mysqlがtemporaryを使用しなければならない理由がわかりません。この場合、filesortを使用します
  2. なぜmysqlは出荷から選択を開始しないのですか?その後、ストアに参加します。それは店から選択を開始し、次に出荷に参加しますか?EXPLAINを実行しようとすると、いつかmysqlがテーブルproduct_storesからselectを開始します
  3. パフォーマンスを向上させるために、テーブル、インデックスを最適化するのを手伝ってください。

(私はmysql 5.0.95を使用しています)

これはテーブル構造です:

4

2 に答える 2

1

クエリ結合戦略では I/O が多すぎるため、クエリは十分に遅くなります

次のように理解するために、クエリ I/O の計算の下書きを作成します。

 1. JOIN shipments (200K records) and product_stores (8K records) 

         200K x 8 K = 1600K I/O
 2. Then, JOIN to stores (45 records)

         1600K x 45 = 75000K I/O

 3. Then, JOIN to districts (698 records)

         75000K x 698 = 50256000K I/O

 4. Then, Filter the result (by storeId), so need to read the result I/O again

         50256000K + 50256000K = **100512000K I/O (TOTAL I/O)** 

 So, total I/O on memory of your query is 100512000K I/O. 

クエリのパフォーマンスを向上させるには、クエリ結合の計画/戦略を再検討する必要があります

例えば:

 1. Read shipments (200K records) and Filter storeId  (assume: result is 8 record)

         200K  + 8 = 208K I/O
 2. Then, JOIN to product_stores (8K records)

         208K x 8K = 1664K I/O

 3. Then, JOIN to stores (45 records)

         1664K x 45K = 74880K I/O

 4. Then, finally JOIN to districts (698 records).

         74880K + 698 = **52266240 I/O (TOTAL I/O)** 

 So, total I/O on memory of your query is 52266240  I/O. (greatly reduce I/O then ever)

したがって、上記の方法を検討することで、クエリのパフォーマンスを向上させることができます。

お役に立てば幸いです。

于 2012-08-30T08:58:59.427 に答える
0

私はちょうど解決策を試しています。これにより実行時間が短縮されることを願っています

SELECT `s`. * , `ps`.`productCode` , `ps`.`productName` , `st`.`name` AS `storeName` , `d`.`name` AS `shipToDistrictName`
FROM `shipments` AS `s`
JOIN `product_stores` AS `ps` ON s.productStoreId = ps.id
JOIN `stores` AS `st` ON (s.storeId = st.id AND s.storeId IN (1, 2, 3, 4, 6, 9, 14, 16, 22, 26, 30))
LEFT JOIN `districts` AS `d` ON s.shipToDistrictId = d.id
GROUP BY `s`.`id`
ORDER BY `s`.`id` DESC
LIMIT 100

これにより、レコードの数が必要なものだけに storeId を持つものだけに制限され、さらに少ない数のレコードで結合が行われるため、実行時間が短縮されます。

それが役に立てば幸い...

于 2012-08-30T06:53:28.557 に答える