B-tree索引对POINT字段无效,因WKB二进制无空间序;必须用SPATIAL INDEX(R-tree)加速ST_*函数;建索引需满足:空间类型、NOT NULL、InnoDB≥5.7.5且innodb_large_prefix=ON;MBRContains快但粗略,需ST_Contains二次精筛;坐标须统一WGS84。
因为MySQL的POINT字段底层是WKB(Well-Known Binary)二进制格式,B-tree索引依赖字典序比较,而经纬度坐标的二进制表示在字典序上毫无空间意义——两个地理上相邻的点,其WKB值可能天差地别。哪怕你给location加了INDEX(location),EXPLAIN里依然显示type: ALL,就是全表扫描。
没有SPATIAL INDEX时,所有基于空间关系的函数都会退化为逐行计算:
ST_Contains()要遍历每条记录,解析几何对象再做包含判断ST_Distance_Sphere()得对每条记录算一次球面距离,再排序取TOP N而R-tree空间索引能把地理空间划分成嵌套矩形块,查询时直接剪枝掉明显不重叠的区域,把扫描量从O(n)降到接近O(log n)。
常见失败不是语法错,而是漏掉任一约束:
POINT、POLYGON),不能是TEXT或JSON存坐标字符串NOT NULL——MySQL强制要求,否则CREATE SPATIAL INDEX直接报错innodb_large_prefix=ON
正确写法示例:CREATE TABLE shops (id INT, loc POINT NOT NULL, SPATIAL INDEX(loc)) ENGINE=InnoDB;
如果你只需要粗略筛选(比如地图瓦片加载、POI初步过滤),MBRContains()比ST_Contains()快一个数量级,因为它只比较最小包围矩形(Minimum Bounding Rectangle),不校验真实几何边界。
但这也意味着:
ST_Contains()二次精筛,尤其在业务逻辑要求精确包含时真正容易被忽略的点是:空间索引本身不解决投影问题。所有坐标必须统一用WGS84(EPSG:4326)存入,否则ST_Distance_Sphere()返回的距离会严重失真——这点连很多DBA都会在上线后才踩坑。