【问题标题】:mysql strange performance anomaly with left join左连接的mysql奇怪的性能异常
【发布时间】:2013-12-16 22:08:22
【问题描述】:

我有以下简单的左连接查询:

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 行。我需要加入所有表,因为我需要一些字段。

问题是,如何优化第二个查询以更好地执行?

这里是解释:

这是快速查询:

+----+-------------+-------+-------------+------------------------------+------------------------------+---------+----------------+------+--------------------------------------------------------+
| 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 字段中搜索用户在搜索表单中输入的每个单词。 例如,如果用户输入“x y z”,我需要选择 h.hely_nev 等于 x 或 y 或 z 且 h.hely_telepules 等于 x 或 y 或 z 等的每条记录。

【问题讨论】:

  • 使用 2 个查询而不是 1 个 - 太多或会影响性能

标签: mysql sql performance indexing database-performance


【解决方案1】:

归根结底,这是因为在第一种情况下,查询优化器能够使用helyek 上的索引来确定只有两个可能的候选行。

当您在 szakmak 上添加 OR 条件时,您不允许在 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

【讨论】:

  • 谢谢,对不起:(我忘了说:我需要使用多个子句组。(编辑问题)
  • @user974250 这最终变成了一个非常丑陋的查询。对我来说,您可能希望查看您的架构以寻求解决方案。如果您必须以如此复杂的方式过滤记录,我想知道是否有更好的方法来关联这些行。从您的示例看来,您正在寻找确定行必须具有跨三个字段的三个可能值之一的情况,但每条记录必须在每个位置具有唯一值。 (有点像策划者之谜)。这是因为您使用 AND 连接这些过滤条件。
  • 是的,没错。这就是为什么我需要用 AND 加入条件,这就是为什么我不能分隔条件。
猜你喜欢
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 1970-01-01
相关资源
最近更新 更多