不少开发者都曾踩过这个坑:在MySQL中,普通B-tree索引对POINT字段完全不起作用。原因其实很清晰——MySQL的POINT字段底层采用WKB(Well-Known Binary)二进制格式,而B-tree索引依赖于字典序比较。问题在于,经纬度坐标的二进制表示在字典序上毫无空间意义:两个地理上相邻的点,其WKB值可能天差地别。即便你给location字段添加了INDEX(location),EXPLAIN结果中依然显示type: ALL,即全表扫描。这并非Bug,而是由底层数据结构决定的本质限制。

ST_*系列函数必须依赖SPATIAL INDEX,否则性能极差
ST_Contains、ST_Distance_Sphere这类空间查询,如果没有SPATIAL INDEX,就会退化为逐行计算,代价高昂:
- ST_Contains()需要遍历每条记录,解析几何对象后再做包含判断,效率极低
- ST_Distance_Sphere()得对每条记录计算一次球面距离,再排序取TOP N,数据量大时几乎不可用
- 哪怕只是查询“5公里内商户”,10万条数据也意味着10万次浮点运算外加全表I/O——这种速度,实际体验可想而知
而R-tree空间索引的工作原理完全不同:它将地理空间划分为嵌套的矩形块,查询时直接剪枝掉明显不重叠的区域,扫描量从O(n)骤降至接近O(log n)。这才是真正的性能加速引擎。
创建SPATIAL INDEX的三大硬性前提,缺一不可
常见的失败原因并非语法错误,而是遗漏了某个关键约束:
- 字段类型必须是空间类型(如POINT、POLYGON),不能是TEXT或JSON中存储的坐标字符串
- 字段必须声明NOT NULL——MySQL强制要求,否则CREATE SPATIAL INDEX会直接报错
- 存储引擎必须支持:MyISAM原生支持;InnoDB从5.7起支持,但要求MySQL ≥ 5.7.5且innodb_large_prefix=ON
正确写法示例:CREATE TABLE shops (id INT, loc POINT NOT NULL, SPATIAL INDEX(loc)) ENGINE=InnoDB;
MBRContains速度快但精度粗糙,需警惕陷阱
如果你只需要粗略筛选,比如地图瓦片加载或POI初步过滤,MBRContains()比ST_Contains()快一个数量级。原因很简单:它只比较最小包围矩形(Minimum Bounding Rectangle),不校验真实几何边界。
但这也意味着:
- 多边形凹陷区域内的点可能被遗漏
- 细长L形区域会被包围进一个很大的矩形,导致召回大量误匹配
- 务必搭配ST_Contains()进行二次精筛——尤其在业务逻辑要求精确包含时,这一步绝对不能省略
还有一个容易被忽略的点:空间索引本身并不解决投影问题。所有坐标必须统一使用WGS84(EPSG:4326)存入,否则ST_Distance_Sphere()返回的距离会严重失真。这一点,连不少DBA都是上线后才踩坑发现的。
