简体   繁体   English

mysql奇怪的性能异常与左连接

[英]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. 每个_id和h.hely_nev,h.hely_telepules都被编入索引,并且在0.0008秒内运行。

But if I add one more where clause (OR sz.szakma_id = 1) the speed drops down to 0.7 sec! 但是如果我再添加一个where子句(或者sz.szakma_id = 1),速度会下降到0.7秒! 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. helyek,eladok,eladok_rel_szakmak中的50k行,szakmak中只有30行。 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: 这是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 ) 我看到第二个查询不能使用任何键,但我不知道为什么(sz.szakma_id字段有一个索引)

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. 目标是在h.hely_nev,h.hely_telepules中搜索用户在搜索表单中输入的每个单词的sz.szakma_id字段。 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. 例如,如果用户输入“xyz”,我需要选择h.hely_nev等于x或y或z的每个记录,并且h.hely_telepules等于x或y或z,依此类推。

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. 在它的根源,这是因为在第一种情况下,查询优化器能够使用helyek上的索引来确定只有两个可能的候选行。

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. 当您添加或条件上szakmak ,你不允许在使用索引helvek为缩小的可能结果集的目的。 You would probably be best served to do a UNION of the results of two seperate queries, one with conditions: 你可能最好做两个单独查询结果的UNION,其中一个有条件:

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 如果您认为szakmak表的基数较少(并且给定过滤条件的行数较多),那么您也可以使用一系列正确的连接而不是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: 有关LEFT / RIGHT JOIN优化的MySQL文档,请参阅此处的更多信息:

http://dev.mysql.com/doc/refman/5.6/en/left-join-optimization.html 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