簡體   English   中英

mysql奇怪的性能異常與左連接

[英]mysql strange performance anomaly with left join

我有以下簡單的左連接查詢:

SELECT SQL_NO_CACHE * FROM helyek h 
    LEFT JOIN eladok e ON e.elado_id = h.elado_id 
    LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id 
    LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id 
WHERE  h.hely_nev = 'xy'
    OR h.hely_telepules = 'xy'

每個_id和h.hely_nev,h.hely_telepules都被編入索引,並且在0.0008秒內運行。

但是如果我再添加一個where子句(或者sz.szakma_id = 1),速度會下降到0.7秒! 這真的很慢。

SELECT SQL_NO_CACHE * FROM helyek h 
    LEFT JOIN eladok e ON e.elado_id = h.elado_id 
    LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id 
    LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id 
WHERE  h.hely_nev = 'xy'
    OR h.hely_telepules = 'xy'
    OR sz.szakma_id = 1

helyek,eladok,eladok_rel_szakmak中的50k行,szakmak中只有30行。 我需要加入所有表格,因為我需要一些領域。

問題是,如何優化第二個查詢以更好地執行?

這是EXPLAINs:

這是快速查詢:

+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
| id | select_type | table |    type     |        possible_keys         |             key              | key_len |      ref       | rows |                         Extra                          |
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
|  1 | SIMPLE      | h     | index_merge | idxhelynev,idxhely_telepules | idxhelynev,idxhely_telepules | 482,482 | NULL           |    2 | Using union(idxhelynev,idxhely_telepules); Using where |
|  1 | SIMPLE      | e     | eq_ref      | PRIMARY                      | PRIMARY                      | 4       | h.elado_id     |    1 |                                                        |
|  1 | SIMPLE      | ersz  | ref         | elado_id                     | elado_id                     | 4       | e.elado_id     |    1 |                                                        |
|  1 | SIMPLE      | sz    | eq_ref      | PRIMARY                      | PRIMARY                      | 4       | ersz.szakma_id |    1 |                                                        |
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+    

這很慢:

+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
| id | select_type | table |  type  |        possible_keys         |   key    | key_len |      ref       |    rows     |    Extra    |
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
|  1 | SIMPLE      | h     | ALL    | idxhelynev,idxhely_telepules | NULL     | NULL    | NULL           | 54326       |             |
|  1 | SIMPLE      | e     | eq_ref | PRIMARY                      | PRIMARY  | 4       | h.elado_id     |           1 |             |
|  1 | SIMPLE      | ersz  | ref    | elado_id                     | elado_id | 4       | e.elado_id     |           1 |             |
|  1 | SIMPLE      | sz    | eq_ref | PRIMARY                      | PRIMARY  | 4       | ersz.szakma_id |           1 | Using where |
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+ 

我看到第二個查詢不能使用任何鍵,但我不知道為什么(sz.szakma_id字段有一個索引)

編輯:我忘了提到:我需要使用多個子句組。 像這樣:

(h.hely_nev = 'x' OR h.hely_telepules = 'x' OR sz.szakma_id = x)
AND
(h.hely_nev = 'y' OR h.hely_telepules = 'y' OR sz.szakma_id = y)
AND
(h.hely_nev = 'z' OR h.hely_telepules = 'z' OR sz.szakma_id = z)

這就是為什么我不能使用兩個單獨的查詢。 目標是在h.hely_nev,h.hely_telepules中搜索用戶在搜索表單中輸入的每個單詞的sz.szakma_id字段。 例如,如果用戶輸入“xyz”,我需要選擇h.hely_nev等於x或y或z的每個記錄,並且h.hely_telepules等於x或y或z,依此類推。

在它的根源,這是因為在第一種情況下,查詢優化器能夠使用helyek上的索引來確定只有兩個可能的候選行。

當您添加或條件上szakmak ,你不允許在使用索引helvek為縮小的可能結果集的目的。 你可能最好做兩個單獨查詢結果的UNION,其中一個有條件:

WHERE  h.hely_nev = 'xy'
OR h.hely_telepules = 'xy'

和另一個條件

WHERE sz.szakma_id = 1

所以類似於:

SELECT SQL_NO_CACHE * FROM helyek h 
    LEFT JOIN eladok e ON e.elado_id = h.elado_id 
    LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id 
    LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id 
WHERE  h.hely_nev = 'xy'
    OR h.hely_telepules = 'xy'
UNION DISTINCT
SELECT SQL_NO_CACHE * FROM helyek h 
    LEFT JOIN eladok e ON e.elado_id = h.elado_id 
    LEFT JOIN eladok_rel_szakmak ersz ON ersz.elado_id = e.elado_id 
    LEFT JOIN szakmak sz ON sz.szakma_id = ersz.szakma_id 
WHERE sz.szakma_id = 1

如果您認為szakmak表的基數較少(並且給定過濾條件的行數較多),那么您也可以使用一系列正確的連接而不是helyek

所以你像這樣翻轉查詢:

SELECT SQL_NO_CACHE *
FROM
    szakmak sz 
    RIGHT JOIN eladok_rel_szakmak ersz ON sz.szakma_id = ersz.szakma_id
    RIGHT JOIN eladok e ON ersz.elado_id = e.elado_id
    RIGHT JOIN helyek h ON e.elado_id = h.elado_id 
WHERE  h.hely_nev = 'xy'
    OR h.hely_telepules = 'xy'
    OR sz.szakma_id = 1

這將改變表依賴順序。 我不確定哪種方式最適合你。

有關LEFT / RIGHT JOIN優化的MySQL文檔,請參閱此處的更多信息:

http://dev.mysql.com/doc/refman/5.6/en/left-join-optimization.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM