未使用空间索引
Posted
技术标签:
【中文标题】未使用空间索引【英文标题】:Spatial Index not being used 【发布时间】:2016-05-07 17:23:29 【问题描述】:我在 GEO_LOCATION 列上有一个空间索引,但是当我执行 EXPLAIN 时,它并没有显示该索引正在被使用。谁能告诉我为什么?
EXPLAIN
SELECT AsText(GEO_LOCATION)
FROM PERSON
WHERE ST_Distance(POINT(-94.0724223,38.0234332), GEO_LOCATION) <= 10
id:1 选择类型:简单 表:人 类型:全部 可能的键:NULL 键:NULL key_len: NULL 参考:空 行数:612602 额外:使用 where
这是我的环境: 服务器类型:MariaDB 服务器版本:10.1.8-MariaDB - mariadb.org 二进制分发 协议版本:10 服务器字符集:UTF-8 Unicode (utf8) Apache/2.4.17 (Win32) OpenSSL/1.0.2d php/5.6.14 数据库客户端版本:libmysql - mysqlnd 5.0.11-dev - 20120503 PHP 扩展:mysqli 文档 PHP版本:5.6.14
【问题讨论】:
【参考方案1】:很遗憾,ST_Distance() < threshold
不是 sargable 搜索条件。为了满足这个查询,MySQL 必须计算表中每一行的函数值,然后将其与阈值进行比较。所以它必须进行全表扫描(或者可能是全索引扫描)。
要利用索引来加速此查询,您将需要一个边界框标准。查询要复杂得多,但也快得多。假设几何图形中的 x/y 点以度数表示纬度/经度,则查询可能如下所示:
set @latpoint = 38.0234332;
set @lngpoint = -94.0724223;
set @r = 10.0; /* ten mile radius */
set @units=69.0; /* 69 statute miles per degree */
SELECT AsText(geo)
FROM markers
WHERE MbrContains(GeomFromText(
CONCAT('LINESTRING(', @latpoint-(@r/@units),' ',
@lngpoint-(@r /(@units* COS(RADIANS(@latpoint)))),
',',
@latpoint+(@r/@units) ,' ',
@lngpoint+(@r /(@units * COS(RADIANS(@latpoint)))),
')')),
geo)
这是如何工作的?一方面,MbrContains(bound,item) 函数是 sargable。另一方面,又大又丑的 concat 项目产生了一条从边界矩形的西南角到东北角的对角线。使用您的数据点和十英里半径,它看起来像这样。
LINESTRING(37.8785 -94.2564,38.1684 -93.8884)
当您在MbrContains()
的第一个参数中使用该对角线的GeomFromText()
渲染时,它用作边界矩形。然后MbrContains()
可以利用漂亮的四叉树几何索引。
第三,ST_Distance()
,在 MySQL 中,不处理大圆经纬度计算。 (PostgreSQL 有一个更全面的GIS extension。)MySQL 就像平地里的烙饼一样愚蠢。它假定几何对象中的点以平面几何表示。所以 ST_Distance() < 10.0
带有 lng/lat 点做了一些奇怪的事情。
此查询生成的结果存在一个缺陷;它返回边界框中的所有点,而不仅仅是在指定半径内。这可以通过单独的距离计算来解决。我已经写了所有这些in some detail here。
注意:对于 GPS 分辨率的经纬度,32 位 FLOAT
数据具有足够的精度。 DOUBLE
是 MySQL 的地理扩展使用的。当您以度为单位工作时,小数点后五位以上超出了 GPS 的精度。 DECIMAL()
不是 lat/lng 坐标的理想数据类型。
【讨论】:
哇。多么丰富的信息啊!今天晚些时候会仔细看看这个。完全有道理。谢谢奥利!我一直在寻找这些信息。 在我今天早上跑去参加我女儿的篮球比赛之前必须做一个小测试(今晚会更深入一些)。因此,即使我在 10 英里内输入已知的纬度/经度,它也不会返回任何匹配项。我已经尝试了几个已知点,但仍然没有。 如果表中的点使用 SRID(空间参考标识符),请确保添加与 GeomFromText() 函数的第二个(可选)参数相同的 SRID:例如GeomFromText('...', 4326)
。
当使用 4326 的 SRID(或类似的我猜)时,空间数据被定义为 X=lon 和 Y=lat,所以需要使用@latpoint
而不是@lngpoint
,反之亦然,在上面的代码中。
上面边界坐标的计算不太准确:janmatuschek.de/…以上是关于未使用空间索引的主要内容,如果未能解决你的问题,请参考以下文章