说真的,刚入行搞 Elasticsearch 的时候,我也觉得 geo_point 就是个简单的经纬度字段,随便建个索引就能跑。直到上个月,我们那个基于 LBS 的社交项目突然崩了,查询延迟从几十毫秒飙到几秒,运维小哥差点把服务器砸了。那一刻我才明白,es 存储 geo point 这事儿,真不是点几下鼠标那么简单,里面全是坑。
很多兄弟喜欢用 text 或者 keyword 类型去存经纬度,觉得这样能搜到。我告诉你,这是典型的偷懒思维,后期维护能把你逼疯。geo_point 是 ES 专门为地理位置设计的类型,它底层用了 Geohash 或者 Double-Geohash 来压缩数据,既能做范围查询,又能算距离,还能做地图可视化。你要是不用它,等于开着法拉利在泥地里跑,动力全浪费。
咱们先聊聊精度问题。我见过不少项目,为了省空间,把精度设得极低,结果导致用户明明在隔壁街,搜索出来却在隔壁市。这体验,谁受得了?根据我的实测数据,在大多数城市级应用中,保留小数点后 5 位到 6 位就足够了。比如北京朝阳区某小区,精度到 6 位大概误差在 1 米以内,这对绝大多数业务场景完全够用。如果你非要追求厘米级精度,那得考虑用 Double 类型配合自定义脚本,但那样查询性能会断崖式下跌,得不偿失。
再说说存储格式。很多人不知道,geo_point 支持多种输入格式,比如数组 [lon, lat],或者字符串 "lat,lon",甚至对象形式。我推荐用对象形式,因为可读性最强,而且后续扩展方便。比如 {"lat": 39.9042, "lon": 116.4074}。这样你在排查问题时,一眼就能看出问题出在哪。要是用数组,一旦顺序搞反,数据就全乱了,那时候哭都来不及。
还有个容易被忽视的点,就是索引映射中的 index 参数。默认情况下,geo_point 字段是可以被索引和搜索的,但如果你只需要存储不需要搜索,可以把 index 设为 false,这样能节省不少磁盘空间。不过,大多数情况下,我们肯定是要做附近的人、附近的热搜这些功能的,所以还是老老实实让它可搜索吧。
在实际操作中,我还发现一个常见误区,就是忽视地理坐标系。WGS84 是国际标准,GPS 设备输出的数据通常就是这个。但国内有些地图服务商,比如高德、百度,用的是 GCJ-02 或 BD-09 坐标系。如果你直接把百度地图的数据存进 ES,不做转换,那误差能大到让你怀疑人生。我有个案例,某外卖平台因为没做坐标转换,导致骑手定位偏差超过 500 米,用户投诉率直线上升。所以,数据入库前,务必做好坐标系的转换和清洗工作。
另外,关于性能优化,除了合理的索引设计,分片策略也很关键。如果你们的地理位置数据量巨大,比如全国范围的门店数据,建议按区域进行分片,或者使用 alias 来管理不同区域的数据。这样查询时,ES 只需要扫描相关的分片,速度自然就上去了。别指望一个超大分片能扛住所有查询,那只会让集群负载过高,最终导致雪崩。
最后,我想说的是,es 存储 geo point 不仅仅是技术问题,更是业务理解的问题。你要清楚你的用户到底需要什么精度的定位,需要多快的响应速度,这样才能做出最合适的技术选型。别盲目追求高大上的架构,适合业务的才是最好的。希望这篇文章能帮大家在避坑的路上少摔几跤,毕竟,代码是写给人看的,顺便给机器执行,对吧?
总结一下,选对类型、控制精度、注意坐标系、优化索引,这四步走稳了,你的 geo_point 查询就能飞起来。别等出了问题再修,预防永远比治疗便宜。