说实话,以前做定位功能全靠Java层算距离,跑起来CPU飙高不说,用户还嫌卡。后来硬着头皮去折腾MySQL的geo索引 mysql相关字段,才发现这玩意儿真不是随便建个索引就行的。今天就把我踩过的那些坑和最后落地的方案分享出来,希望能帮你省点时间。
首先得明确一点,MySQL 5.7之后才正式支持地理空间类型,如果你还在用老版本,那真是白忙活。我们用的5.7,建表的时候直接指定sr_id=0,千万别忘这个参数,不然数据进去了根本没法用空间函数查询。我当时就犯了这个迷糊,建了表插了数据,一查全是空的,折腾了一下午才反应过来。
第一步就是定义字段。别再用varchar存经纬度了,虽然看着方便,但性能差得离谱。直接用POINT类型,配合SRID 0创建几何列。建好表后,记得一定要在这个列上建SPATIAL索引,不然那个ST_Contains或者MBRContains这些函数就跑不动了,等于白搞。
第二步是数据写入。这里有个大坑,很多兄弟直接从字符串转Point,结果发现精度不够或者报错。建议在后端直接生成WKT格式,然后调用ST_GeomFromText('POINT(lon lat)')来构造对象。注意是lon在前lat在后,很多人搞反了导致点位全飘到海洋里了,查半天查不出问题,最后才发现是坐标顺序错了。
第三步是索引策略。这里得看你的业务场景。如果是查某个点周围半径内的用户,那肯定是用ST_Distance_Sphere或者ST_Within来筛选。但纯走geo索引 mysql的边界索引(MBR索引)其实只能做粗过滤,真正精确的距离计算还是要靠函数计算。所以我建议的做法是,先用空间索引缩小范围,比如查出一个大方框内的用户,然后在内存里或者SQL里再用ST_Distance_Sphere算精确距离排序。这样效率比全表扫距离快太多了。
第四步就是查询优化了。我发现一个特别反直觉的现象,如果WHERE条件里只有空间函数没有普通索引列配合,有时候全表扫描反而比走空间索引快。特别是数据量小于几万条的时候。所以别迷信索引,得自己explain看执行计划。我试过在geo索引 mysql字段上加where limit语句,发现回表太多反而变慢,后来改成先取出id列表再in查询,速度提升了不止一倍。
还有一点很容易忽略,就是索引的维护。MySQL的空间索引是基于R树的,数据插入和删除频繁的话,树会频繁分裂合并,导致写性能下降。如果你的业务是高频写入,比如实时上报轨迹,建议做个缓冲,攒一批再写入,或者考虑分表。不要傻乎乎地单条插入,那样索引重建的开销能把你搞死。
最后提醒下版本兼容性。8.0的空间函数比5.7丰富很多,而且优化器更聪明。如果你的架构允许升级,强烈建议上8.0。我在8.0上跑同样的geo索引 mysql查询,某些复杂场景下快了不少。当然升级有风险,得测好回滚方案。
其实技术没有银弹,geo索引 mysql也不是万能的。关键在于理解它的原理,知道它什么时候有用,什么时候没用。多测试,多看执行计划,别光盯着文档看。希望这些实战经验能对你有点帮助,有问题咱们评论区再聊。】