必须加SPATIAL INDEX,否则ST_Contains、ST_Distance_Sphere等查询将全表扫描;因POINT是WKB二进制数据,B-tree按字典序比较而地理坐标无序,R-tree空间索引才支持多维几何范围划分。

必须加 SPATIAL INDEX,否则所有 ST_Contains、ST_Distance_Sphere 类查询基本等于全表扫描。
为什么普通 B-tree 索引对地理字段完全无效
MySQL 的 POINT 字段本质是二进制 WKB 数据,B-tree 索引按字典序比较,而地理坐标的二进制表示毫无顺序可言。哪怕你给 location 字段加了普通 INDEX,执行 EXPLAIN 也会显示 type: ALL —— 就是全表扫。
空间索引用的是 R-tree 结构,专门处理多维几何对象的范围划分。它能把整个地理空间切成嵌套矩形块,查询时快速跳过明显不重叠的区域。
常见错误现象:
- 建表时漏写
SPATIAL INDEX (location),只写了INDEX (location) - 用
ALTER TABLE ADD INDEX添加普通索引,误以为能加速地理查询 - 字段类型是
GEOMETRY但未指定NOT NULL,导致SPATIAL INDEX创建失败(MySQL 要求空间索引字段必须NOT NULL)
创建空间索引的正确写法和限制
不是所有语句都能成功建空间索引。必须满足三要素:字段类型是空间类型、字段非空、建表或 ALTER 时显式声明 SPATIAL INDEX。
正确示例:
CREATE TABLE merchants ( id INT PRIMARY KEY, name VARCHAR(100), location POINT NOT NULL, SPATIAL INDEX (location) );
已有表补加空间索引:
ALTER TABLE merchants ADD SPATIAL INDEX (location);
关键限制:
- 一张表只能有一个
SPATIAL INDEX(MySQL 限制,不是你写错) - 不能在
TEXT、VARCHAR或普通数字字段上加SPATIAL INDEX,会报错ERROR 1170 (42000): BLOB/TEXT column 'xxx' used in key specification without a key length - 如果字段是
GEOMETRY类型,插入前必须用ST_GeomFromText()或ST_PointFromText()构造,直接插字符串会存成 NULL,索引就失效了
查询语句怎么写才能真正用上空间索引
光有索引不够,查询写法不对照样白搭。核心原则:先做边界框粗筛,再做精确计算。
错误写法(无法走索引):
SELECT * FROM merchants
WHERE ST_Distance_Sphere(location, ST_PointFromText('POINT(116.397428 39.90923)')) <p>优化写法(两步走):</p><pre class="brush:php;toolbar:false;">SELECT * FROM merchants
WHERE location && ST_MakeEnvelope(
116.397428 - 0.05, 39.90923 - 0.05,
116.397428 + 0.05, 39.90923 + 0.05,
4326
)
AND ST_Distance_Sphere(location, ST_PointFromText('POINT(116.397428 39.90923)')) <p>说明:</p>
-
&&是 MySQL 的 MBR(最小边界矩形)相交操作符,极快,且强制使用SPATIAL INDEX -
ST_MakeEnvelope生成一个近似 5km 宽的矩形(纬度每度约 111km,经度需按纬度缩放;这里 0.05° 是经验值,实际应按ST_Distance_Sphere反推) - 第二层
ST_Distance_Sphere做球面距离精筛,只对第一步筛选出的少量行执行
容易被忽略的兼容性与精度陷阱
MySQL 空间函数默认用 WGS84 坐标系(SRID 4326),但很多旧数据没显式设 SRID。这会导致两种后果:
- 插入时用
ST_PointFromText('POINT(116.397428 39.90923)')不带 SRID,MySQL 默认存为 SRID 0,后续所有空间函数可能返回 NULL 或错误结果 - 查询时
ST_MakeEnvelope必须显式传入 SRID(如4326),否则和location字段 SRID 不匹配,&&判断直接失效 - 如果表里混着不同 SRID 的数据(比如部分从 Shapefile 导入时用了 3857),空间索引会退化,甚至报错
ER_SPATIAL_NOT_SUPPORTED
最稳妥做法:建表时明确字段 SRID,插入时统一用带 SRID 的构造函数,例如 ST_PointFromText('POINT(116.397428 39.90923)', 4326)。











