别整那些虚头巴脑的理论了,直接上干货。
我在geo这行摸爬滚打七年,见过太多人拿着Elasticsearch(简称es)搞地理位置检索,结果要么慢得像蜗牛,要么内存直接爆掉。今天不聊高大上的架构,就聊聊怎么用最少的钱、最稳的方式,把es geo检索这事儿办漂亮。
先说个真事儿。去年有个做同城配送的客户找我,说他们es集群每天查询量不大,但响应时间要200毫秒以内。结果我去一看,好家伙,他用了默认的distance_field,而且没做分片优化。每次查附近的人,整个集群都在抖。这种低级错误,新手最容易犯。
咱们第一步,得选对字段类型。
很多新人觉得,坐标随便存个字符串或者double就行。大错特错!必须用geo_point类型。这是es专门为地理位置设计的,底层用了GeoHash或者Geohash64算法,查询速度比你自己算距离快几个数量级。别省这点配置,后期改数据结构,那痛苦程度能让你怀疑人生。
第二步,分片策略要讲究。
geo检索对分片很敏感。如果你的数据量在千万级,别搞太多分片。我见过有人为了追求并行,开了50个分片,结果每次查询都要协调50个节点,网络IO直接打满。一般来说,单个分片控制在10GB-30GB比较合适。对于geo数据,如果热点明显,比如市中心数据密集,郊区稀疏,可以考虑用自定义分片路由,把热点数据分散开,或者单独建一个索引处理高频查询。
第三步,查询语句别乱写。
很多开发者喜欢用bool查询套geo_distance。这没问题,但要注意参数。radius别设太大,比如你查“附近5公里”,别传50000米,直接传5km。单位统一用米或者千米,别混用,容易出错。还有,别忘了加track_total_hits。默认情况下,es只返回前10条结果,如果你要统计总数,必须显式开启,否则前端显示“共0条”或者“共10条”,用户体验极差。
再说个避坑点:内存溢出。
geo检索是CPU和内存密集型操作。如果你用geo_shape类型做复杂的多边形查询,比如查某个行政区内的所有门店,那对内存要求极高。我有个客户,查一个市的范围,直接OOM(内存溢出)。解决办法是什么?简化几何形状。把复杂的行政区划简化成几个大的多边形,或者用预先计算好的网格ID(Grid ID)来过滤。虽然精度会损失一点点,但查询速度提升十倍不止。对于大多数业务场景,这点精度损失完全可以接受。
最后,监控不能少。
上线后,盯着慢查询日志。es有专门的slowlog配置,把阈值设低一点,比如1秒。一旦有查询超过这个时间,立刻告警。我见过太多项目,上线前跑得欢,上线后因为一个复杂的geo查询拖垮整个集群。定期review这些慢查询,优化索引结构,或者加缓存。
记住,es geo检索不是万能的。如果你的数据量达到亿级,且查询复杂度极高,考虑把geo数据同步到专门的地理数据库,比如PostGIS,或者用Redis的Geo模块做热点数据缓存。es负责全文检索和聚合,Redis负责毫秒级附近搜索,分工明确,各司其职。
别迷信单一技术栈。真正的专家,是知道什么时候该用什么工具的人。
希望这些经验能帮你少走弯路。geo检索这事儿,细节决定成败。多测试,多监控,别等到线上出问题了再后悔。
本文关键词:es geo检索