【问题标题】:Optimizing mysql query to select all points with in polygon using spatial indexes优化 mysql 查询以使用空间索引选择多边形中的所有点
【发布时间】:2018-11-07 01:10:13
【问题描述】:

首先,我承认我在空间函数方面的经验非常少。我在 MySQL 中有一个表,其中包含 20 个字段和 23549187 条包含地理数据的记录。其中一个字段是“point”,它是点数据类型,并在其上具有空间索引。我有一个查询选择多边形内的所有点,如下所示,

select * from `table_name` where ST_CONTAINS(ST_GEOMFROMTEXT('POLYGON((151.186 -23.497,151.207 -23.505,151.178 -23.496,151.174 -23.49800000000001,151.176 -23.496,151.179 -23.49500000000002,151.186 -23.497))'), `point`)

这很好用,因为多边形很小。但是,如果多边形变得很大,执行时间会变得非常慢,并且迄今为止最慢的查询运行了 15 分钟。添加索引确实有助于将其缩短到 15 分钟,否则将需要将近一个小时。我可以在这里做些什么来进一步改进。 此查询将由作为守护程序运行的 PHP 脚本运行,我担心这种缓慢的查询是否会导致 MySQL 服务器停机。

欢迎提出任何使其变得更好的建议。谢谢。

编辑:

show create table;

CREATE TABLE `table_name` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `lat` float(12,6) DEFAULT NULL,
  `long` float(12,6) DEFAULT NULL,
  `point` point NOT NULL,
  PRIMARY KEY (`id`),
  KEY `lat` (`lat`,`long`),
  SPATIAL KEY `sp_index` (`point`)
) ENGINE=MyISAM AUTO_INCREMENT=47222773 DEFAULT CHARSET=utf8mb4

还有几个字段我不应该在这里透露,但是过滤器赢了

解释慢查询的 sql 输出:

+----+-------------+------------+------+------- --------+------+---------+------+----------+------ --------+
|编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 |
+----+-------------+------------+------+---------- -----+------+---------+------+----------+--------- ----+
| 1 |简单 |表名 |全部 |空 |空 |空 |空 | 23549187 |使用位置 |
+----+-------------+------------+------+---------- -----+------+---------+------+----------+--------- ----+

用较小的多边形解释查询的 sql 输出,

+----+-------------+------------+-------+------ ------+---------+---------+------+------+----- --------+
|编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 |
+----+-------------+------------+-------+--------- ------+----------+---------+------+------+-------- -----+
| 1 |简单 |表名 |范围 | sp_index | sp_index | 34 |空 | 1 |使用位置 |
+----+-------------+------------+-------+--------- ------+----------+---------+------+------+-------- -----+

看起来最大的多边形没有使用索引。

【问题讨论】:

  • 将其移至dba.stackexchange.comgis.stackexchange.com 可能会更好
  • 对于大规模,你的意思是大尺寸(大边界框,就像覆盖整个地球),和/或数千个点的复杂形状,和/或包含很多/所有的积分? MySQL 似乎已经决定使用后者,您能否验证是这种情况(例如,您在结果集中获得了 2300 万个点中的很大一部分)?
  • 慢的一个或两个?更重要的问题是它里面有多少点,特别是多边形的边界框中有多少点(如果你在多边形周围做一个正方形,里面会有多少点 - 这是索引的部分提供)。虽然可能没有一种简单的方法可以加快查询速度,但对多边形形状有一个大致的了解会有所帮助(请参阅主教的回答):如果您的多边形例如描述一个圆,你可以通过使用一个内部正方形(便于索引)来消除 60% 的比较,然后将剩余的 4 个圆部分合并到它。
  • 这些数字很大。为 6k 多边形检查可能有 500k 点(位于多边形附近)将花费大量时间。我不确定您是否可以从一般角度优化您的查询。您可能需要结合其他有关您的情况的因素。例如:如果您的多边形是佛罗里达州的 6k 多边形,并且您想知道其中有哪些客户,您可以存储/预先计算某人住在迈阿密,然后您只需要检查迈阿密是否在佛罗里达州即可获得一次比较中 30% 的结果。
  • 或者也许你可以简化你的多边形,因为你不需要 4k 点来详细描述基韦斯特的海岸线,因为没有合理的期望有人真的住在墨西哥的高尔夫球场,所以10 点而不是 4k 可能就足够了。您的问题可能会有这样的聚类或推理,艰巨的任务可能是找到一个。您需要添加有关您的问题的详细信息,和/或您可能想在gis.stackexchange.com 上描述您的问题,也许他们有一个想法。

标签: php mysql sql spatial-query spatial-index


【解决方案1】:

MySQL 使用R-Trees 来索引空间数据。与B-Tree indexes 一样,这些对于针对总数的一小部分的查询来说是最佳的。随着您的边界多边形变大,可能匹配的数量会增加,并且在某些时候,优化器决定切换到全表扫描更有效。这似乎是这里的场景,我看到了三个选项:

首先,尝试将LIMIT 添加到您的查询中。通常,如果优化器断定在全表扫描中会发生较少的 I/O 搜索,MySQL 会忽略索引。但是,至少使用 B-Tree 索引,MySQL 将短路该逻辑并始终在存在 LIMIT 时执行 B-Tree 潜水。我假设 R-Tree 也有类似的短路。

第二个,在精神上与第一个相似,尝试forcing MySQL to use the index。这指示 MySQL 表扫描比优化器决定的更昂贵。了解优化器只有启发式,并不真正知道超出其内部统计数据得出的“昂贵”程度。我们人类有直觉,有时 - 有时 - 知道得更好。

select * force index (`sp_index`) from `table_name` where ST_CONTAINS(ST_GEOMFROMTEXT('POLYGON((151.186 -23.497,151.207 -23.505,151.178 -23.496,151.174 -23.49800000000001,151.176 -23.496,151.179 -23.49500000000002,151.186 -23.497))'), `point`)

最后,如果这些都不起作用,那么您需要做的是将边界多边形分解为更小的多边形。例如,如果您的边界多边形是每边 500 公里的正方形,则将其分成每边 250 公里的 4 个正方形,或每边 125 公里的 16 个正方形,等等。然后UNION 所有这些一起。索引会在每一个上使用,累积结果可能会更快。 (注意将UNION 放在一起很重要:MySQL 不能对空间查询应用多个范围扫描。)

【讨论】:

  • 这真的很丰富。我确实尝试强制索引但没有任何区别,实际上延迟了执行。我会尝试找出一种方法来打破多边形并进行联合。因为它不是一直都是正方形,所以不会那么容易。
  • @RohithMohan There are algorithms for partitioning polygons. 基本思想是分成面积接近相等的两半,然后在每一半上重复该过程,但需要多少次。
猜你喜欢
  • 2016-06-14
  • 1970-01-01
  • 2020-09-15
  • 2011-11-09
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多