首先,我承认我在空间功能方面的经验非常少。我在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 | +----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
看起来最大的多边形不使用索引。
答案 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不能对空间查询应用多个范围扫描。)