用POINT+SRID4326而非两个DECIMAL字段,因地球是球面、平面距离公式单位错误致偏差超200公里;MySQL无法对独立lat/lon建有效空间索引;后续地理操作(如围栏判断)无函数支持,维护成本高。

为什么用 POINT + SRID 4326 而不是两个 DECIMAL 字段
直接结论:存 lat 和 lon 两个浮点字段,再用 SQRT(POW(lat-?,2) + POW(lon-?,2)) 算距离,查得慢、结果错、索引无效——这不是优化问题,是模型错误。
根本原因有三:
- 地球是球面,平面距离公式单位是“度”,不是“米”,北京到东京按它算可能偏差超 200 公里;
- MySQL 对独立
lat/lon字段无法构建空间索引,WHERE lat BETWEEN ? AND ? AND lon BETWEEN ? AND ?仍大概率全表扫描; - 后续要做“是否在围栏内”“是否相交”等操作时,纯数值字段无函数支持,只能应用层硬写,维护成本爆炸。
建表与插入时必须注意的三个硬性条件
空间索引不是“加了就生效”,它对字段定义极其敏感。漏掉任一条件,SPATIAL INDEX 就是摆设。
- 字段类型必须是空间类型(如
POINT),不能是GEOMETRY或普通数字类型; - 字段必须声明
NOT NULL,MySQL 明确要求空间索引字段非空,否则ALTER TABLE ... ADD SPATIAL INDEX直接报错; - 插入数据必须用
ST_GeomFromText('POINT(long lat)', 4326)构造,漏掉第二个参数(SRID)会默认为 0,导致所有地理函数(如ST_Distance_Sphere())返回NULL。
常见翻车点:POINT 参数顺序是 经度在前、纬度在后,上海是 POINT(121.4737 31.2304),不是 POINT(31.2304 121.4737)。
哪些查询能真正走空间索引
空间索引只对特定函数和写法生效,不是所有含 ST_ 前缀的函数都走索引。
-
ST_Contains(poly_col, point_col)可走索引,但前提是poly_col是常量或来自小结果集(比如一个已知围栏多边形); -
ST_Distance_Sphere()本身不走索引,必须配合前置范围剪枝,例如先用MBRContains(ST_MakeEnvelope(...), coordinates)快速过滤出候选点,再用ST_Distance_Sphere()精算; -
ST_Intersects()需搭配MBRIntersects()做 MBR 前置判断,否则执行计划中key列不会显示索引名,type也不会是range或ref。
验证是否生效:用 EXPLAIN 看执行计划,key 列必须显示你的空间索引名,且 type 是 range 或 ref;若仍是 ALL,大概率是函数用反了或字段顺序错了。
一张表只能有一个 SPATIAL INDEX,且不能回退成普通索引
这是 MySQL 的硬限制,不是配置问题。即使你删掉空间索引,再试图加 INDEX(location),也解决不了地理查询性能——普通 B-tree 索引对空间数据无效。
关键细节:
- 已有表补加空间索引,必须用
ALTER TABLE t ADD SPATIAL INDEX (location),不能用ADD INDEX; - 如果字段原是
GEOMETRY类型,需先MODIFY成POINT NOT NULL SRID 4326,再加索引; - 空间索引只加速几何关系类查询(包含、邻近、相交),对
ORDER BY ST_X(coordinates)这类纯坐标提取操作无加速作用。
最容易被忽略的是:空间索引对“单点精确匹配”无意义,它的价值完全体现在“区域筛选”和“邻近搜索”场景中——如果你的业务几乎全是查“某个ID的位置”,那空间索引反而增加写入开销,不值得上。


















