本文关键词:es geo性能
干ES这行快十年了,见过太多人因为一个地理位置查询把集群搞崩。很多人一上来就搞海量数据,结果查个“附近的人”要好几秒,用户直接跑光。其实,es geo性能这事儿,真没那么玄乎,关键是你得懂它的脾气。
我见过最典型的坑,就是随便扔一堆经纬度进去,也不管索引结构,也不管查询方式。Es底层用的是GeoHash或者Geohash64,这玩意儿是把二维坐标压成一维字符串。距离越近,前缀越像。但这有个前提,你得选对字段类型。
第一步,别乱用keyword。很多人觉得keyword万能,但在做地理位置聚合或者范围查询时,keyword简直就是灾难。你必须用geo_point类型。这点没得商量。如果你现在的数据已经是keyword,那没办法,只能重建索引。别偷懒,偷懒的代价是后期运维哭都来不及。
第二步,检查你的mapping。很多新手写mapping,把lat和lon分开存,或者用了text类型。记住,geo_point是复合类型,内部会处理坐标。如果你的数据量特别大,比如亿级,建议开启doc_values,但要注意,geo_point默认是开启的,别手贱去关了。关了你就等着查数据全表扫描吧。
再说说查询优化。很多兄弟喜欢用match_all然后过滤,或者在应用层算距离。这太蠢了。ES内置了geo_distance和geo_bounding_box。geo_distance适合查半径,比如“5公里内”;geo_bounding_box适合查矩形区域,比如“某个商圈”。别混着用。混着用不仅慢,还容易算错。
还有一个隐藏的性能杀手,就是查询时的聚合。很多人喜欢在做geo查询的同时,搞terms聚合。比如“查附近100人,并按年龄分组”。这操作在数据量大时,内存爆炸是迟早的事。建议把聚合拆出来,或者限制聚合的返回数量。别贪多,贪多嚼不烂。
另外,分片策略也很关键。地理位置数据通常具有空间局部性。如果你的分片策略是按时间滚动,那每次查询可能都要扫过所有分片。虽然ES有全局文档计数优化,但物理上的分散还是会影响效率。如果业务允许,可以考虑按区域分片,或者使用更细粒度的索引策略。比如,把不同城市的数据分到不同的索引里,查询时只查目标城市的索引。这招虽然笨,但真管用。
说到这儿,还得提一下缓存。ES的查询缓存默认是开的,但如果你频繁查不同的半径,缓存命中率可能不高。这时候,可以考虑在应用层加一层Redis缓存。比如,先查Redis,没有再查ES。但这有个前提,你的数据更新频率不能太高。如果数据每秒都在变,那缓存就是摆设,反而增加复杂度。
最后,监控不能少。别等慢查询报警了才去查。用Kibana的Dev Tools,看看你的查询耗时分布。如果某个查询经常超过1秒,那就得优化了。可能是索引没建好,可能是查询语句写得烂,也可能是硬件瓶颈。一步步排查,别盲目加机器。加机器解决不了逻辑问题,只会让问题变得更隐蔽。
其实,es geo性能的核心就两点:数据模型要对,查询语句要精。别指望有什么魔法按钮,能一键提升性能。都是靠细节堆出来的。
如果你现在正被地理位置查询慢折磨,或者不知道该怎么优化你的mapping,不妨停下来想想上面的步骤。对照检查一下,说不定就能找到问题所在。要是实在搞不定,或者想聊聊更深层的架构设计,欢迎随时来聊。咱们一起把性能提上去,把系统稳住。毕竟,做技术嘛,就是为了解决问题,不是为了制造焦虑。