【问题标题】:Mysql bad execution planMysql错误的执行计划
【发布时间】:2019-02-12 13:11:40
【问题描述】:

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

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

我的执行时间是 0.1 秒

+----+-------------+-------+------------+-------+---------------------------------+--------------------+---------+------------------------------+-------+----------+---------------------------------------------------------------------+
| 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 行。似乎在第一种情况下测试了太多行,但在第二种情况下过滤是有效的:地理定位是一个过滤器,应该在加入之前应用:1)你得到满足条件的地方 2)你发现租金有那些通过匹配表来放置 geolocalisation_id。 请赐教。

【问题讨论】:

  • 哪个表包含couchage
  • 沙发在餐桌上
  • INDEX(categorid_id, couchage) 呢?

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


【解决方案1】:

知道过滤地理位置之前比之后更聪明,因为你知道一些 MySQL 不知道的关于你的数据和查询的信息。

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

事实上,连我也只知道,因为你告诉我,现在可以根据列名假设gauche总是小于droite,所以你在g上的条件可能描述得很窄窗户。但是 MySQL 不知道,因为您没有告诉 MySQL,所以它不能考虑到这一点。当然,它也没有能力根据列名的含义做出决定。

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

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

也许可以将您的信息编码为spatial data(一个点)和一个索引。然后你可以寻找位于二维矩形中的点,MySQL 现在可以理解这实际上是一个包含有限数据量的非常小的矩形。不需要你的数据实际上是几何数据,你可以将它编码成二维。

假设我的假设是正确的,你也许还可以使用(g.gauche = 151579 or g.gauche = 151580),而MySQL也应该能够理解这只是有限的数据量。

您当然可以只强制索引(或使用FROM geolocalisation g STRAIGHT_JOIN annonce a)。你知道一些 MySQL 不知道的东西,而且通常你不能告诉 MySQL。缺点是这不能适应其他情况,例如如果您(偶尔)在查询中使用更大的窗口,或者 gauche &lt;= droite 不再正确。

【讨论】:

  • 好的。首先,gauche 总是比 droite 小(法语中的意思是左和右):地理系统是一棵树,根是“世界”,所以“左/右”技巧允许您选择任何地理区域和所有它的后代。在这个例子中,它只是一个城镇,所以只有一个节点,左右差为1。其次,“gauche”和“droite”是索引值。第三,mysql评估一个计划与另一个计划的复杂性,并选择最便宜的一个。但是怎么会这么误会呢? geolocalisation_id 是表 g 的主键,为什么要以表 a 开头呢?
  • 我知道它是左右的,我的意思是 MySQL 没有。假设 ID 从 1 变为 1.000.000。如果你说:gauge &gt;= 100.000,MySQL 可以说:哦,好吧,可能所有行的 9/10 都在此之上(实际上并不是那么精确,如果它是唯一索引会更好,所以你应该尽可能使用一个,它可能会改善您的统计数据),但您可能会得到yist。如果你说droite &lt;= 100.001,它可能会说:这可能意味着大约有 1/10 的行都在那里。它无法理解它只有 1 个条目,因为它不知道 left &lt; right
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
相关资源
最近更新 更多