[英]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.