【问题标题】:Why is my query suboptimal?为什么我的查询不是最理想的?
【发布时间】:2015-08-06 18:21:03
【问题描述】:

我有一个从 Django 服务器访问的 MySQL InnoDB 数据库。我有这张桌子:

+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| areasymbol   | varchar(255) | NO   |     | NULL    |                |
| spatialver   | int(11)      | YES  |     | NULL    |                |
| lkey         | int(11)      | YES  |     | NULL    |                |
| musym        | varchar(255) | NO   |     | NULL    |                |
| mukey        | int(11)      | YES  |     | NULL    |                |
| featsym      | varchar(255) | NO   |     | NULL    |                |
| featkey      | int(11)      | YES  |     | NULL    |                |
| north        | double       | YES  | MUL | NULL    |                |
| south        | double       | YES  | MUL | NULL    |                |
| east         | double       | YES  | MUL | NULL    |                |
| west         | double       | YES  | MUL | NULL    |                |
| soil_type_id | int(11)      | YES  | MUL | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

该表目前包含约 7-8 百万行,我预计完成后它的行数至少会增加 3 倍。这是一个静态表。我们每隔一段时间就会进行导入以向其中添加内容,但没有任何内容被修改或删除。

+-----------------+------------+----------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table           | Non_unique | Key_name                         | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------+------------+----------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| soil_soilregion |          0 | PRIMARY                          |            1 | id           | A         |     7657769 |     NULL | NULL   |      | BTREE      |         |               |
| soil_soilregion |          1 | soil_soilregion_e733fdfc         |            1 | soil_type_id | A         |          15 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | north_soilregion                 |            1 | north        | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | south_soilregion                 |            1 | south        | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | east_soilregion                  |            1 | east         | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | west_soilregion                  |            1 | west         | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | north_south_east_west_soilregion |            1 | north        | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | north_south_east_west_soilregion |            2 | south        | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | north_south_east_west_soilregion |            3 | east         | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
| soil_soilregion |          1 | north_south_east_west_soilregion |            4 | west         | A         |     7657769 |     NULL | NULL   | YES  | BTREE      |         |               |
+-----------------+------------+----------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

我有一个北/南/东/西坐标的方框,我正在寻找可能与该方框重叠的任何区域

当我在数据库上运行这个查询时:

select *
from soil_soilregion
where east > -86.8379775155 AND north > 40.3782334957 AND
      south < 40.3817576747 AND west < -86.8240119179;

大约需要 10 秒,这是不可接受的。当我使用解释时,这就是它告诉我的:

+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+
| id | select_type | table           | type | possible_keys                                                                                      | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | soil_soilregion | ALL  | north_soilregion,south_soilregion,east_soilregion,west_soilregion,north_south_east_west_soilregion | NULL | NULL    | NULL | 7657769 | Using where |
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+

当我在数据库上运行这个查询时:

select *
from soil_soilregion
where east > -86.8379775155 AND east < -85.8379775155 AND
      north > 40.3782334957 AND north < 41.3782334957 AND
      south < 40.3817576747 AND south > 39.3817576747 AND
      west < -86.8240119179 AND west > -87.8040119189;

大约需要 6-7 秒。这更好,但仍然不是最理想的。这段代码仍然可以工作,因为没有物体的高度或宽度超过 1(所以我给它在每个方向上的最大距离为 1)。

我有几个问题:

  1. 为什么第一个查询不使用索引? (我认为这是因为该范围内的潜在项目太多)
  2. 为什么它从不使用我的复合索引?那不是最理想的吗?
  3. 我能做些什么来改进这个查询或我的索引吗?

注意:使用力指数只会产生负面影响。

谢谢!

编辑 1: 根据建议,我将查询更改为与复合索引的顺序相同,这就是我得到的:

explain select * from soil_soilregion  where north > 40.3782334957 AND south < 40.3817576747 AND east > -86.8379775155 AND west < -86.8240119179; 
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+
| id | select_type | table           | type | possible_keys                                                                                      | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | soil_soilregion | ALL  | north_soilregion,south_soilregion,east_soilregion,west_soilregion,north_south_east_west_soilregion | NULL | NULL    | NULL | 7657769 | Using where |
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+

【问题讨论】:

  • 你尝试在哪里改变语句的顺序?北、南、东、西?
  • @JamieD77 - 如果您重新排序 WHERE 子句,优化器不会做任何不同的事情。
  • 请提供SHOW CREATE TABLE;它比DESCRIBE更具描述性。

标签: mysql sql django innodb


【解决方案1】:

您的查询的问题在于您的不平等。唉,这些限制了索引的使用——每次索引查找最多一个不等式。

你需要解决这个问题的数据结构是多维索引。在 SQL 数据库中,这通常是使用 GIS 扩展提供的,记录在 here

没有这些扩展,您可以尝试神秘的智慧。我可以想出解决这个问题的一种方法,但它会使表和查询都变得更复杂一些。为东和北添加一个新列,它是一个整数:eastinorthi。然后,在easti, northi 上建立索引。并且,将查询写为:

select *
from ((select sr.*
       from soil_soilregion sr
       where easti = -86 and northi in (40, 41)
      ) union all
      (select sr.*
       from soil_soilregion sr
       where easti = -85 and northi in (40, 41)
      ) 
     ) sr
where east > -86.8379775155 AND north > 40.3782334957 AND
      south < 40.3817576747 AND west < -86.8240119179;

子查询会将所有内容放在一个相对较小的框中。然后由外部查询过滤。子查询应该使用索引,所以应该很快。

考虑到您要查找的内容的大小,对于整数转换,使用一个度数的分数甚至比一个度数更好。

【讨论】:

【解决方案2】:

短期但部分修复是有一个“覆盖指数”。也就是说,制作一个包含边界框的索引,加上 id(也许还有土壤类型?)。然后这样做:

SELECT b.*
    FROM (
        SELECT id FROM soilregion
            WHERE east... AND west ... AND ...
         ) AS a
    JOIN soilregion AS b ON b.id = a.id;

这可能会加快查询速度,因为:

  • 索引就是子查询中所需的全部内容
  • 索引小于数据
  • 子查询完成后,有一个ids 的简短列表,可以在真实表中轻松快速地查找(通过JOIN)。

您的一些“为什么”问题

  • 各个索引仅消除了 7M 行中的一部分(如“此处以东的所有内容”)。这没有多大帮助。此外,当一个索引“无用”时,它就不会被使用——简单地扫描表会更快。

  • 复合索引(南北...)并没有做得更好。这是因为它从north 的范围测试开始,并且无法通过。

  • 第二次尝试“似乎”更快——这可能是因为缓存,而不是因为它更好。

解决方案?...

计划 A:空间索引,正如 Gordon 提到的那样。

B 计划:重构数据以使用伪二维索引方法,在我的 "find the nearest pizza parlors" 博客中进行了描述。一个问题:我还没想好如何适应“重叠”而不是“最近”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    • 2011-02-19
    • 2011-01-12
    相关资源
    最近更新 更多