本文关键词:_geo_distance
做 Geo 这行十一年了,真见过太多因为一个距离计算搞崩服务器,或者算出个离谱结果被老板骂得狗血淋头的惨案。
今天不扯那些高大上的理论,就聊聊最实在的_geo_distance。
很多新手一听到“附近的人”或者“周边搜索”,第一反应就是拉表、用公式算。
我告诉你,千万别这么干。
你想想,如果用户量上来,每秒几千个请求,你那 CPU 还能转得动吗?
我前年接手的一个项目,就是典型的反面教材。
当时为了赶工期,直接用 SQL 的 Haversine 公式硬算。
结果上线第三天,数据库直接拖死,线上事故。
老板脸都绿了,问我怎么解决。
这时候就得祭出 Elasticsearch 里的_geo_distance 这个神器了。
它底层用的是 Geohash 或者 QuadTree 技术,能把地球表面划分成网格。
这样查询的时候,不用全表扫描,直接定位到附近的网格,速度提升不止一个量级。
但是,这里有个大坑,很多人都不知道。
就是单位的问题。
在 ES 里,_geo_distance 默认的单位是米。
但你有时候需要公里,或者英里,这时候如果不注意转换,算出来的距离能差出一万倍。
我有一次帮客户排查问题,发现他查出来的“附近 500 米”的店,实际距离有 5 公里。
查了半天日志,才发现他在 DSL 里忘了写 unit,或者写错了缩写。
这种低级错误,真的让人想砸键盘。
还有啊,很多人喜欢用 geo_point 存经纬度,这没错。
但如果你存的是 geo_shape,比如一个多边形的商圈范围,那玩法又不一样了。
这时候_geo_distance 可能就不太好使了,得配合 geo_bounding_box 或者 geo_polygon 用。
我见过一个做外卖平台的哥们,因为没搞懂这个,导致骑手接单范围经常错乱。
有的骑手明明在城东,却接到了城西的订单,因为距离计算偏差太大。
后来我们重新梳理了数据结构,把核心商圈用 geo_shape 存起来,外围用 geo_point。
这样既保证了精度,又兼顾了性能。
再说说性能优化。
如果你要查“1 公里内”的所有商家,建议加上 filter 上下文。
别把它放在 query 上下文里,那样会算相关性得分,浪费资源。
直接用 filter,只判断是否在范围内,不计算分数,速度飞快。
这点细节,很多教程里都不提,都是靠踩坑踩出来的。
另外,数据精度也是个问题。
经纬度保留几位小数?
一般来说,保留 6 位小数,精度大概在 1 米左右。
再多了,存储压力大,意义也不大。
除非你是做高精度导航,否则没必要追求极致精度。
我有个朋友,非要保留 8 位小数,结果索引文件大得吓人,查询慢得像蜗牛。
真是得不偿失。
最后,提醒一下大家,测试的时候,一定要用真实数据。
别拿几条假数据测测就上线,真到了高并发场景,各种边界情况才会暴露出来。
比如极点附近的计算,或者跨越国际日期变更线的情况,都得考虑到。
虽然_geo_distance 很强大,但它也不是万能的。
对于超大规模的数据,还是得结合分片策略,合理设计索引。
总之,做 Geo 开发,细节决定成败。
希望这篇帖子能帮你们避避坑,少加点班。
毕竟,头发掉得越少,代码写得越好,对吧?
哈哈,开个玩笑。
希望能帮到正在头疼距离计算的你。