是,MySQL 5.7.5+ 的 InnoDB 支持空间索引,要求引擎为 InnoDB、列类型为 NOT NULL 的 GEOMETRY 子类型(如 POINT),并使用 SPATIAL KEY 显式创建 R-tree 索引。

MySQL 5.7中InnoDB是否支持空间索引?
支持,但必须确认版本 ≥ 5.7.5 且表使用 InnoDB 引擎。早于 5.7.5 的版本中,ST_* 函数(如 ST_Distance、ST_Contains)仅在 MyISAM 上可用;5.7.5 起,InnoDB 完整支持空间数据类型(POINT、POLYGON 等)和 R-Tree 索引。
验证方式:SHOW CREATE TABLE t1 查看引擎和列类型;执行 SELECT ST_Distance(POINT(0,0), POINT(1,1)) 不报错即说明函数可用。
创建InnoDB空间索引的正确写法
不能直接用 INDEX 或 KEY,必须显式指定 SPATIAL KEY,且字段必须为 NOT NULL。
-
POINT列必须定义为NOT NULL,否则建索引会失败并报错:ERROR 1167 (HY000): The used storage engine can't index column 'loc' - 语法必须是:
SPATIAL KEY `spatial_idx` (`loc`),不能写成KEY `spatial_idx` (`loc`) - 不支持前缀长度(如
loc(25)),R-Tree 索引要求完整值参与构建 - 示例建表语句:
CREATE TABLE places ( id INT PRIMARY KEY, name VARCHAR(100), loc POINT NOT NULL, SPATIAL KEY `spatial_idx` (`loc`) ) ENGINE=InnoDB;
查询时为什么ST_*函数仍很慢?
即使索引存在,常见原因不是索引没生效,而是查询写法绕过了空间索引:
- 使用
WHERE ST_Distance(loc, POINT(x,y)) < r—— 这是全表扫描,ST_Distance是计算函数,无法利用索引 - 正确写法应使用
MBRContains+ST_Within组合:WHERE MBRContains( LineString(Point(x-r, y-r), Point(x+r, y+r)), loc ) AND ST_Distance(loc, POINT(x,y)) < r,先用 MBR 快速过滤,再精算 -
ST_Within、ST_Contains、ST_Intersects可直触 R-Tree,但输入必须是几何对象,不能是动态构造的 WKT 字符串(如ST_Within(loc, 'POLYGON((...))')会触发隐式转换,可能失效) - 确保
loc列的 SRID 与查询中一致(如都为 0 或都为 4326),SRID 不匹配会导致索引跳过
从MyISAM迁移到InnoDB空间表的关键陷阱
迁移不是改个 ENGINE=InnoDB 就完事,以下三点极易被忽略:
-
MyISAM允许NULL空间列建SPATIAL索引,InnoDB不允许——迁移前必须UPDATE SET loc = POINT(0,0) WHERE loc IS NULL并加NOT NULL约束 -
MyISAM的空间索引对WKT字符串容忍度高,InnoDB更严格:插入POINT(180 90)合法,但POINT(180,90)(逗号分隔)会报错ERROR 1367 (22007): Illegal non geometric 'POINT(180,90)' value found -
LOAD DATA INFILE导入含空间字段的数据时,不能用字符串直接赋值给POINT列,需用SET loc = POINTFROMTEXT(@wkt)或SET loc = POINT(@x, @y)显式转换
最常被漏掉的是 SRID 一致性检查和 NOT NULL 强制——这两项不处理,SPATIAL KEY 建不起来,后续所有优化都无从谈起。


















