搞地图搜索和附近的人功能,你是不是也被es geo性能折磨得掉头发?这篇文直接告诉你怎么调参、怎么建索引,保证你的查询速度从秒级降到毫秒级,别再让老板催进度了。
说实话,刚接手这个需求的时候,我也觉得挺简单,不就是加个geo_point字段嘛。结果上线一测,好家伙,查询延迟直接飙到两秒以上,用户骂声一片。后来我扒了无数文档,又跟几个大厂的朋友聊了聊,才摸清这里面的门道。今天就把这些血泪经验分享出来,希望能帮兄弟们避坑。
首先,建索引的时候千万别偷懒。很多人为了省事,直接把经纬度存成text或者keyword,这绝对是找死。必须用geo_point类型,而且要注意,如果你的数据量特别大,比如亿级别,那得考虑用geo_shape或者嵌套结构,不过大多数场景下,标准的geo_point配合合理的mapping就够用了。这里有个小细节,就是distance_type参数,默认是arc,如果你追求极致速度且数据分布比较集中,可以试试plane,但精度会稍微牺牲一点,这个得自己权衡。
再来说说查询策略。很多兄弟喜欢用geo_distance查询,这没问题,但如果你的半径很大,比如方圆50公里,那查询压力会非常大。这时候,建议先用geo_bounding_box做个粗筛,框定一个大致的矩形区域,然后再在这个小范围内做精确的距离计算。这样能大幅减少参与计算的数据量,es geo性能立马就能提上来。我有个朋友做的LBS应用,就是这么改的,QPS直接翻了五倍,老板高兴得请他吃了顿火锅。
还有一个容易被忽视的点,就是分片策略。别搞太多分片,也别太少。一般来说,每个分片存20G到50G数据比较合适。如果你的数据量不大,却分了上百个分片,那查询时的开销全花在协调节点上了,纯属浪费资源。另外,副本数也要看情况,读多写少可以多加几个副本,提升读取性能;要是写压力大,那就少搞点副本,别让磁盘IO成为瓶颈。
说到这儿,还得提一下缓存。Elasticsearch自带的缓存机制其实挺强大的,但前提是你要用得对。比如,对于热点区域的查询,结果是可以缓存的。你可以设置query cache,这样同样的查询条件第二次来的时候,直接返回结果,不用再去磁盘里翻找。当然,缓存不是万能的,如果数据更新频繁,缓存命中率低,那还不如不开。
最后,监控不能少。别等崩了才想起来看日志。用Kibana搭个仪表盘,实时监控CPU、内存、磁盘IO以及查询延迟。一旦发现有慢查询,立马分析原因。是索引没建好?还是查询语句写得烂?或者是硬件资源不够?对症下药,才能药到病除。
总之,es geo性能优化是个系统工程,不是一蹴而就的。得结合业务场景,不断调整参数,不断优化代码。别指望有个万能公式,适合自己的才是最好的。
如果你还在为查询慢发愁,或者不知道该怎么优化mapping,欢迎私信聊聊。咱们可以一起看看你的具体场景,说不定能帮你省下不少加班时间。毕竟,早点下班回家陪陪家人,不比在办公室盯着日志强?