优化mysql查询以使用空间索引选择多边形中的所有点

时间:2018-11-07 01:10:13

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

首先,我承认我在空间功能方面的经验非常少。我在MySQL中有一个表,其中包含20个字段和23549187个包含地理数据的记录。字段之一是“点”,它是点数据类型,上面有空间索引。我有一个查询,选择一个看起来像这样的多边形内的所有点,

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输出:

+----+-------------+------------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+------------+------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | table_name | ALL  | NULL          | NULL | NULL    | NULL | 23549187 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+----------+-------------+

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

+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | table_name | range | sp_index      | sp_index | 34      | NULL |    1 | Using where |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+

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

1 个答案:

答案 0 :(得分:2)

MySQL使用R-Trees为空间数据编制索引。像B-Tree indexes一样,它们最适合以总数的一小部分为目标的查询。随着边界多边形变大,可能匹配的数目也增加,并且在某些时候,优化器决定切换到全表扫描更为有效。这似乎是这里的场景,我看到三个选项:

首先,尝试向您的查询添加LIMIT。通常,如果优化器认为在全表扫描中发生较少的I / O寻道,则MySQL将忽略该索引。但是,至少使用B树索引,MySQL会短路该逻辑,并在出现LIMIT时始终执行B树潜水。我假设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 km的正方形,请将其分成每边250 km的4个正方形或每边125 km的16个正方形,依此类推。然后UNION将所有这些合在一起。该索引将用于每个索引,并且累积结果可能会更快。 (请注意,UNION一起使用很重要:MySQL不能对空间查询应用多个范围扫描。)