简体   繁体   中英

mysql strange performance anomaly with left join

I have the following simple left join query:

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'

Every _id and the h.hely_nev,h.hely_telepules is indexed and it runs under 0.0008 sec.

But if I add one more where clause (OR sz.szakma_id = 1) the speed drops down to 0.7 sec! this is really slow.

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

50k rows in helyek, eladok, eladok_rel_szakmak and only 30 rows in szakmak. I need to join all tables because i need some field from all.

The question is, how can i optimize the second query to perform better?

Here are the EXPLAINs:

This is the fast query:

+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
| 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 |                                                        |
+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+    

This is the slow:

+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+
| 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 |
+----+-------------+-------+--------+------------------------------+----------+---------+----------------+-------------+-------------+ 

I see the second query cant use any keys, but i dont know why ( there is an index on the sz.szakma_id field )

EDIT: I forgot to mention: I need to use more than one clause group. Like this:

(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)

Thats why i cant use two seperate queries. The goal is to search in the h.hely_nev, h.hely_telepules ,the sz.szakma_id fields for every word what the user enter in a search form. For example if the user enters "xyz" i need to select every record where the h.hely_nev equals to x or y or z and the h.hely_telepules equals to x or y or z and so on.

At the root of it, it is because in the first case, the query optimizer is able to use the indexes on helyek to determine that there are only two possible candidate rows.

When you add the OR condition on szakmak , you disallow the use of indexes on helvek for purposes of narrowing down the potential result set. You would probably be best served to do a UNION of the results of two seperate queries, one with conditions:

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

and another with condition

WHERE sz.szakma_id = 1

So something like:

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

You might also be able to utilize a series of right joins if you believe the szakmak table with have less cardinality (and more rows for a given filter condition) than helyek

So you flip the query around like this:

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

This would change the table dependency order. I am not sure which would perform best for you.

See more information in MySQL documentation on LEFT/RIGHT JOIN optimization here:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM