做搜索这行七年,我见过太多人把elasticsearch当成数据库用,结果就是集群崩盘,老板骂娘。特别是搞es geo排序的时候,很多人以为只要加上geo_point就能飞起,实际上呢?慢得让你怀疑人生。
上周有个老哥们找我救火,说他们的地图找房系统,用户稍微拖拽一下地图,接口响应就要两三秒。这哪是找房,这是让用户练耐心。我上去一看,好家伙,查询条件里不仅用了geo_distance,还搞了个复杂的bool查询,最后还要按距离排序。这在数据量不大的时候还行,一旦数据量破百万,那简直就是灾难。
咱们得说点实在的。es geo排序的核心痛点,不在于“算得准”,而在于“算得快”。很多新手朋友喜欢用geo_distance查询来过滤,再用sort按距离排序。听着挺逻辑自洽,但elasticsearch底层是怎么做的呢?它得对每个文档计算球面距离。如果没加过滤直接全量排序,那就是全表扫描加距离计算,CPU直接飙到100%。
我有个案例,某本地生活平台,日均查询量几千万。他们最开始的做法是,先查geo_bounding_box缩小范围,然后在内存里算距离排序。结果呢?热点区域查询极快,但边缘区域或者大半径查询,直接超时。后来我们怎么改的?
第一步,别迷信geo_point的精度。对于大多数商业场景,比如找附近的店,精度到小数点后5位就够了,也就是大概10米左右的误差。你非要搞到小数点后7位,那是在浪费内存和计算资源。
第二步,善用geo_distance查询的filter上下文。记住,filter是不参与评分的,而且会被缓存。先把不相关的文档过滤掉,剩下的再排序。别一上来就搞sort,那是在裸奔。
第三步,也是最重要的一点,考虑用geo_hash或者geohash_prefix。对于超大规模数据,有时候把经纬度转换成geohash字符串,利用字符串的前缀匹配来做初步筛选,比直接算距离要快得多。当然,这得看你具体的业务场景,是求近,还是求特定区域内的所有点。
还有个细节,很多人忽略。如果你的数据量特别大,别把所有数据都塞进一个index。按区域分片,或者按时间分片。比如,北京的数据放一个index,上海放一个。这样es geo排序的时候,只需要在局部数据里找,速度自然就上去了。
我见过最蠢的做法,是把所有全国的数据都堆在一个shard里,然后搞个100公里的半径查询。这就像是在一个大海里捞针,还得把每一滴水都检查一遍。合理的分片策略,比如根据城市或者省份,能让查询效率提升好几个数量级。
另外,别忽视内存。geo_point查询比较吃内存,因为要加载doc values。如果heap内存不够,频繁GC,那排序再快也没用。监控你的jvm heap usage,确保有足够的空间给geo查询用。
最后,说句扎心的。别总想着用复杂的查询语法来炫技。最简单的,往往是最快的。先过滤,再排序,尽量用filter上下文,能不用sort就不用,非要用的话,限制返回数量。
这七年,我学到的最大道理就是:技术是为业务服务的,不是为了跑分。es geo排序不是魔法,它是数学和工程学的结合。理解底层原理,才能避开那些坑。希望这些经验,能帮你省下几个通宵加班的夜晚。
本文关键词:es geo排序