MySQL错误执行计划

时间:2018-09-07 08:11:38

标签: mysql query-optimization sql-execution-plan explain

我希望通过解释非常相似的查询来帮助理解数据输出,以及对性能的巨大影响。 我有2张桌子:nononce和地理定位。第一个包含租赁广告,第二个包含相应的位置。因此,我们在给定的位置搜索租金。 如果我使用默认计划

EXPLAIN
SELECT a.*, g.label AS geo_label, g.geo_url
FROM annonce a
INNER JOIN geolocalisation g ON a.geolocalisation_id = g.geolocalisation_id 
WHERE a.categorie_id = 1 AND g.gauche >= 151579 AND g.droite <= 151580 
AND couchage >= 2
ORDER BY FIELD(provenance_id, 2, 1), prix DESC, date_modification DESC, annonce_id ASC

我的执行时间超过10秒。

+----+-------------+-------+------------+--------+---------------------------------+--------------+---------+------------------------------+--------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                   | key          | key_len | ref                          | rows   | filtered | Extra                                              |
+----+-------------+-------+------------+--------+---------------------------------+--------------+---------+------------------------------+--------+----------+----------------------------------------------------+
|  1 | SIMPLE      | a     | NULL       | ref    | geolocalisation_id,categorie_id | categorie_id | 4       | const                        | 502897 |    33.33 | Using index condition; Using where; Using filesort |
|  1 | SIMPLE      | g     | NULL       | eq_ref | PRIMARY,droite,gauche           | PRIMARY      | 4       | vacamax.a.geolocalisation_id |      1 |    25.00 | Using where                                        |
+----+-------------+-------+------------+--------+---------------------------------+--------------+---------+------------------------------+--------+----------+----------------------------------------------------+

如果我强制将地理定位索引设置为“ gauche”

EXPLAIN
SELECT a.*, g.label AS geo_label, g.geo_url
FROM annonce a
INNER JOIN geolocalisation g ON a.geolocalisation_id = g.geolocalisation_id 
WHERE a.categorie_id = 1 AND g.gauche >= 151579 AND g.droite <= 151580 
AND couchage >= 2
ORDER BY FIELD(provenance_id, 2, 1), prix DESC, date_modification DESC, annonce_id ASC

我的执行时间为.1s

+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                   | key                | key_len | ref                          | rows  | filtered | Extra                                                               |
+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+
|  1 | SIMPLE      | g     | NULL       | range | gauche                          | gauche             | 4       | NULL                         | 52785 |    33.33 | Using index condition; Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | a     | NULL       | ref   | geolocalisation_id,categorie_id | geolocalisation_id | 5       | vacamax.g.geolocalisation_id |    13 |    16.66 | Using where                                                         |
+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+

结果是188行。在第一种情况下似乎测试了太多的行,但是在第二种情况下过滤是有效的:geo​​localization是一个过滤器,应在加入之前应用:1)您获得满足条件的地方2)您发现拥有这些条件的租金通过匹配表来放置geolocalisation_id。 请赐教。

1 个答案:

答案 0 :(得分:1)

知道过滤地理位置比之前和之后要聪明,因为您了解有关数据和查询的某些知识,而MySQL却不了解。

具体来说,MySQL猜测它必须在第一个查询中查看502897*1行,并在第二个查询中查看52785*13=686205行,并决定使用第一个查询。决定使用哪种执行计划还有其他因素,但是它使您大致了解MySQL认为数据的外观。这与现实(188行)相距遥远,并且基于如此错误的假设做出决定会导致错误的策略也就不足为奇了。

事实上,即使我只知道,因为您告诉我了,并且现在可以根据列名假设gauche总是小于droite,所以您在{{1} }可能描述了一个非常狭窄的窗口。但是MySQL并不知道这一点,因为您没有告诉MySQL,所以它无法考虑到这一点。当然,它也没有能力根据列名的含义做出决定。

由于您在g上有一个索引,所以对于一个高值(例如gauge),MySQL实际上应该能够发现只有很少的行,并且应该使用更好的执行计划。否则,MySQL基本上是毫无头绪的。尝试在很大范围内更改窗口大小(例如g.gauge >= your_max_value_in_that_column); MySQL不会在g.gauche >= 100000 AND g.droite <= 200000中显示明显不同的数字,除非您接近列的限制(并在列上有索引)。在某些范围内,第一个查询实际上应该变得更快,因为它更接近MySQL假定的数据分布。

那么您如何告诉MySQL您的数据分布?

可能可以将您的信息编码为spatial data(一个点)并在其上进行索引。然后,您可以查找位于2d矩形中的点,MySQL现在可以理解,这实际上是一个非常小的矩形,其中包含有限数量的数据。不需要您的数据实际上就是几何数据,只需要将其编码为二维即可。

假设我的假设是正确的,您也许也可以使用rows,而MySQL也应该能够理解这只是有限的数据量。

您当然可以强制索引(或使用(g.gauche = 151579 or g.gauche = 151580))。您知道MySQL所没有的东西,而且常常不能以其他方式告诉MySQL。缺点是这不能适应其他情况,例如如果您(偶尔)在查询中使用较大的窗口,或者FROM geolocalisation g STRAIGHT_JOIN annonce a不再适用。