做了9年搜索优化,说实话,现在看到有人还在问怎么在ES里查附近的人,我就想叹气。不是技术难,是很多人连基础的数据结构都没搞明白,上来就调API,结果查出来的数据要么不准,要么慢得让人想砸键盘。今天我不讲那些官方的废话,就聊聊我在实战里摸爬滚打出来的经验,希望能帮你们少走点弯路。
首先,你得明白,elasticsearch geo 搜索 的核心不是“算”,而是“筛”。很多人以为ES会实时计算经纬度距离,其实不是。它在索引的时候,就把经纬度转换成了点对象。你如果用的是标准的lat/lon字段,ES默认把它当成一个点。这时候,你要是想查“1公里内”,它得先做一层过滤,再排序。这效率,低得吓人。
我见过太多新手,直接把经纬度存成两个独立的字段,比如lat和lon。然后写查询的时候,用bool query去组合。兄弟,别这么干了。ES有专门的geo_point类型,你用了这个,它底层会构建一个特殊的倒排索引结构。你不用它,就等于开着法拉利去拉煤。
再说说那个让人头大的精度问题。很多客户抱怨,查出来的结果不对,明明就在隔壁小区,怎么给排到后面去了?这里有个坑,就是geo_distance和geo_bounding_box的区别。bounding_box是矩形框,速度快,但边角会有误差;distance是圆形,准,但慢。如果你要的是“附近”,通常用distance。但是,注意看这个参数:distance_type。默认是arc,也就是球面距离,最准。但如果你为了性能,改成plane,那就是平面几何,误差在短距离内可以忽略,但在长距离或者极地附近,简直就是灾难。我之前有个项目,因为没注意这个,导致北方城市的用户查出来的“附近”全是错的,排查了两天,最后发现是这里的问题。
还有,别忽视聚合查询。很多时候,我们不仅要查结果,还要知道每个区域有多少用户。这时候用geo_distance aggregation。这里有个细节,很多文档里没写清楚,就是ranges的边界处理。是包含还是不包含?默认是左闭右开。如果你设置的区间是0-1km,1-2km,那么正好在1km处的点,会跑到1-2km这个桶里。这看似小事,但在做数据报表的时候,会导致总数对不上,或者分布不均。我上次就因为这个,跟产品经理吵了一架,最后发现是聚合边界没对齐。
另外,关于性能优化。如果你的数据量很大,比如千万级,每次做geo查询都全表扫描,那服务器迟早得挂。这时候,你得考虑分片策略。尽量让地理位置相近的数据分布在同一个分片里,或者至少是相邻的分片。但这在ES里很难做到完美,因为ES的分片是基于哈希的。所以,更实际的做法是,使用alias或者index routing,把特定区域的数据路由到特定的索引里。比如,北京的用户查北京的数据,只查北京的索引。这样,elasticsearch geo 搜索 的范围就缩小了,速度自然快。
最后,提一下那个被很多人忽略的geo_shape。如果你查的不是点,而是多边形,比如某个商圈的范围,那就得用geo_shape。这个比geo_point复杂得多,涉及到空间索引的构建。如果你只是简单的点查询,别用这个,性能差很多。但如果你要查“在这个商圈内的所有店铺”,那geo_shape是唯一的选择。
总之,做geo搜索,别光看API文档,得懂底层原理。数据怎么存的,索引怎么建的,查询怎么执行的,心里得有数。不然,出了bug,你连从哪查起都不知道。希望这些经验能帮到正在踩坑的你。记住,技术这东西,光看不练假把式,多试错,多复盘,才是正道。
本文关键词:elasticsearch geo 搜索