真的,我以前对MySQL的空间函数嗤之以鼻。觉得存个纬度经度,两条字符串或者两个DECIMAL字段不就行了吗?非要多此一举搞什么SPATIAL索引、Geometry对象。直到去年搞那个基于LBS的社交推荐项目,几百万用户位置数据一查,我的天哪,那个查询速度慢得我想把服务器拆了扔出去!那一刻我对这种低效存储方式简直是恨之入骨,太浪费生命了!
后来被迫深入研究了geo类型数据 mysql的优化方案,才彻底真香。今天就把我血泪换来的经验揉碎了讲给你听,希望能让你少走点弯路。咱们不整虚的,直接上干货。
先说结论:如果你要做的应用涉及距离计算、范围搜索或者轨迹追踪,别再犹豫,毫不犹豫上MySQL 5.7+支持的GEOGRAPHY或者GEOMETRY类型。为什么?因为精度和性能完全是两个维度的东西。
第一步:建表结构必须规范。
很多新手在这里翻车。你定义字段时,千万别偷懒用varchar存“39.9,116.4”。虽然看着人话,但数据库傻啊。你要明确告诉MySQL这是个几何点。
比如:
ALTER TABLE user_location ADD COLUMN loc POINT SRID 4326;
注意啊,SRID 4326是GPS的标准坐标系,这点极其重要,不然以后算距离全乱套。还有,一定要记得给这个字段加SPATIAL INDEX。不加索引的geo查询在大数据量下就是灾难,我试过不加索引查一次要8秒,加上索引后变成20毫秒以内,这差距简直离谱。
第二步:插入数据要讲究。
别直接把字符串塞进去,容易报错。要用ST_GeoFromText或者类似的函数转换。
INSERT INTO user_location (id, loc) VALUES (1, ST_PointFromText('POINT(116.40 39.90)'));
这里有个坑,顺序是POINT(经度 纬度),别写成纬度经度,很多初学者(包括以前的我)都搞反了,结果地图上点全飞到海里去了,尴尬得想挖个洞钻进去。
第三步:查询时的距离计算。
这是最核心的。用ST_Distance_Sphere函数,它考虑了地球曲率,比简单的欧几里得距离准多了。
SELECT * FROM user_location WHERE ST_Distance_Sphere(loc, Point(116.40, 39.90)) < 1000;
这句代码意思是查找1公里内的数据。实测对比,同样查附近5公里商家,旧方案(两个字段加减法)在高并发下CPU直接飙到100%,而新方案(geo类型)稳如老狗,负载 barely 10%。这性能提升难道不香吗?
我还发现一个现象,很多人以为加了空间索引就万事大吉,其实不然。边界框优化也很重要。在查询前先用MBRContains做预过滤,能减少大量无效计算。
比如先查一个矩形区域,再在结果里算精确距离。虽然多一步,但对于亿级数据,这一步能救命。
说真的,开始学这些东西的时候挺痛苦,文档写得晦涩难懂,英文资料居多。但我坚持啃下来了。现在回想起来,当初那些嘲笑我搞复杂化的同事,现在项目崩盘求我救火的时候,我是真的一点都不想帮。这就是技术带来的底气。
最后总结几点:
1. 坚决抛弃Decimal存经纬度方案,那是上个世纪的事。
2. 务必使用ST_SRID指定坐标系,默认4326别改。
3. 索引必加,且要考虑缓存命中率。
4. 距离计算用ST_Distance_Sphere,别自己写公式,容易算错还被笑话。
技术债终究要还,越早重构越轻松。希望这篇geo类型数据 mysql的实战经验分享,能帮你避开我踩过的坑。毕竟,谁的钱都不是大风刮来的,服务器成本也要省嘛。要是你觉得有用,记得收藏,下次查表时别又忘光了,那我可真要生气了!