加快这个mysql查询

时间:2015-09-02 13:04:39

标签: mysql sql database

我拥有一个包含2000万个地理点记录的庞大数据库,并且每天都在增长。

[id (int)] [group (int)] [latitude (double)] [longitude(double)]
[1]        [1]           [22.365598]         [12.55678]
[2]        [1]           [22.365548]         [12.55238]
[3]        [2]           [24.665348]         [13.10238]

现在我希望在给定的boudingbox中的每个点。boudingbox的大小为南非,查询应该返回大约7.000个结果。但是需要30秒才能得到结果。

查询是:

SELECT distinct(group), id from `table`
where (latitude between -95.22 and 36.458 and longitude between -51.939 and 103.833);

索引是[纬度,经度](btree)。

我怎么能加快速度呢?

修改

我想要完成的事情

数据库包含大量多边形。比如说国家公园。 多边形中的每个节点都位于此表中。现在我想检查国家公园是否在给定位置的范围内。

Id是节点id,组是它所属的多边形,纬度和经度是节点的位置。

当我不使用distinct时,查询将在3秒内完成,但返回900.000结果。在其余代码中要处理的很多。

Sollution

正如戈登·林诺夫(Gordon Linoff)在他的回答中所说:这是一个非常大的表面。该查询用于一些详细的结果。对于这个大表面,我不应该使用所有多边形的所有节点,而是使用多边形的中心线。 当我需要小表面的详细结果时,此查询运行得足够快。

所以我认为我坚持这一点。

4 个答案:

答案 0 :(得分:1)

首先,括号与distinct无关。所以,只需将查询写为:

SELECT distinct `group`, id
from `table`
where latitude between -95.22 and 36.458 and
      longitude between -51.939 and 103.833;

这种类型的查询 - 带有两个between - 并不适合索引。您可以尝试latitude, longitudelongitude, latitude上的索引,并且可能会提供一些小的速度增量。

更好的方法是使用空间索引。 Here是开始了解它们的地方。

然而,即使是空间索引也不太可能有太大帮助。查询中的区域约占地球表面的1/6。如果您的观点是均匀分布的,则需要聚合超过300万条记录(对于select distinct)。你可能没有太多运气来获得这个查询的真正好的表现。

答案 1 :(得分:0)

这不是你问题的直接答案,但是如果你已经使用MySQL 5.5或更高版本并且你可以选择更改数据模型,我建议你使用Point数据类型并添加空间索引

http://dev.mysql.com/doc/refman/5.0/en/using-spatial-data.html

否则我会建议您省略不同的内容,因为它有时会成为性能瓶颈,而是添加group by,我建议也将group包含在索引中。

答案 2 :(得分:0)

如果添加包含整数字段的字段以及确切的teritory索引,该怎么办?防爆。 (latitude between -95.22 and 36.458 and longitude between -51.939 and 103.833); = 1; (some other lat/long span) = 2等。然后,您只需重新计算每条记录的值,并将值存储在新字段中。所有新的(更新的)记录都可以在插入(更新)触发器之前处理,以便为每个附加的(更新的记录)设置位置整数字段。所有SELECT查询都将使用此字段而不是lat / long双字段。 这将是一些数据冗余,但如果您拥有有限的地区列表,可能会对您有所帮助。您可以使用第二个表来存储地区列表及其ID。

答案 3 :(得分:0)

正如The Range Access Method for Multiple-Part Indexes所述:

  

只要比较运算符为=<=>IS NULL,优化程序就会尝试使用其他关键部分来确定间隔。如果运营商是><>=<=!=<>BETWEEN或{{3优化器使用它,但不再考虑关键部分。

换句话说,MySQL仅使用您的索引查找latitude落在指定范围内的记录 - 然后从表中提取这些记录并扫描它们以在longitude上执行过滤。

如果你考虑LIKE的结构如何,那么MySQL这样做的原因是显而易见的:

                          Bd
                 ________/  \_______
                /                   \
               Ad                   Cd
            __/  \__             __/  \__
           /        \           /        \
          Ab        Bb         Cb        Db
         /  \      /  \       /  \      /  \
        Aa  Ac    Ba  Bc     Ca  Cc    Da  Dc

过滤范围的第一个关键部分(例如,上例中第一个字符为BETWEEN 'B' AND 'C',但在您的情况下为纬度标准)非常简单,因为树已经相对于第一个关键部分:

                          Bd
                 ________/  \_______
                /                   \
                \                   Cd
                 \__             __/
                    \           /
                    Bb         Cb
                   /  \       /  \
                  Ba  Bc     Ca  Cc

但是,在第二个关键部分进行过滤时(例如,在此示例中第二个字符为BETWEEN 'b' AND 'c',但在您的情况下为经度标准),生成的已修剪树无法帮助,因为它不是相对于第二个关键部分排序。相比之下,如果第一个关键部分已经过滤精确匹配(而不是范围),则生成的修剪树然后已经被第二个关键部分排序。

因此,B树对于定位多维范围无济于事。 B-tree是一种替代数据结构,更适合此类问题。 MySQL能够使用其R-tree

创建R树索引
  1. 创建一个spatial extensions的新列(例如POINT),该列将保存您的坐标数据并spatial data type

    ALTER TABLE `table`
      ADD coordinates POINT,
      ADD SPATIAL INDEX (coordinates);
    
  2. 从现有数据中填充该列:

    UPDATE `table` SET coordinates = Point(longitude, latitude);
    

    您可能希望定义触发器和/或视图以协助进一步迁移。

  3. 执行搜索:

    SELECT DISTINCT `group`, id 
    FROM   `table`
    WHERE  MBRContains(
             MultiPoint(Point(-51.939, -95.22), Point(103.833, 36.458)),
             coordinates
           )
    

    这种方法特别好用的是,从MySQL 5.6.1开始,您可以index执行更精确的搜索:例如定义准确代表国界的多边形。

  4. 更新您的应用程序以使用此新列,例如:

    SELECT X(coordinates) AS longitude, Y(coordinates) AS latitude FROM `table`
    

    您可能希望定义触发器和/或视图以协助迁移。

  5. 删除旧列:

    ALTER TABLE `table` DROP longitude, DROP latitude;
    
  6. 但是,您应该注意到MySQL的空间扩展使用欧几里德几何(显然,地球是球形的):这不应该影响上述操作,但要小心使用它来执行计算比如距离。