做定位服务三年,踩过最大的坑不是代码写崩,而是以为ES能扛住所有并发。上周给一个本地生活平台做重构,老系统查询附近商家要800毫秒,用户骂娘,老板拍桌子。我接手后,把延迟压到了40毫秒以内。中间没少交学费,今天把真金白银换来的经验摊开说,希望能帮兄弟们省点头发。
很多人一上来就搞全量扫描,或者把经纬度直接存成text类型,这绝对是自杀行为。正确的姿势是用geo_point类型,这是ES专门为地理位置设计的。但光用对类型还不够,关键在索引结构和查询策略。
先说索引映射。别偷懒,必须显式声明mapping。我见过太多人用动态映射,结果某天数据量上来,类型推断错误,整个集群直接OOM。对于elasticsearchnet geo 这种高频查询场景,建议把经纬度字段单独拎出来,并且设置doc_values为true,虽然geo_point默认就是true,但显式声明能避免后续维护时的扯皮。另外,shard的数量别设太多,初期建议1个主分片,测试环境足够用,生产环境根据数据量定,一般单节点50G-100G数据比较舒服。
查询环节是重灾区。很多开发者喜欢用match_all加filter,或者用wildcard去搜周边,这效率低得吓人。要用geo_distance查询,并且配合pre_filter_shard_size参数。这个参数默认是-1,意味着所有分片都会参与计算,数据量大时直接卡死。我把它设为100,让ES先在小范围内过滤,再精确计算,速度提升至少3倍。
再说说.NET客户端的问题。Nest和Elasticsearch.Net这两个库,很多人搞混。Nest封装得好,但性能略低;Elasticsearch.Net更底层,速度快但写起来麻烦。如果你追求极致性能,比如我们要处理每秒上万次的周边搜索,建议直接用Elasticsearch.Net,手动构造Request Body。别嫌麻烦,反序列化那几毫秒在高并发下就是瓶颈。
这里有个坑,geo_distance查询时,单位别搞错。默认是米,但你如果传了千米,结果会差一千倍。我在调试时,发现查出来的距离全是几百公里,查了半天代码,最后发现是前端传参时单位搞混了。这种低级错误,排查起来能让人怀疑人生。
还有缓存策略。ES本身不是缓存,别指望它扛住所有读请求。对于热点商圈,比如市中心5公里内的商家,数据变化不大,完全可以加一层Redis缓存。Key用经纬度网格+半径,Value序列化商家列表。这样90%的请求直接走Redis,ES只处理那10%的长尾查询。这套组合拳打下来,QPS轻松过万。
最后说个容易被忽视的点:数据倾斜。如果你的用户主要集中在北上广深,而服务器在各地均匀分布,那这几个地区的分片负载会极高。解决办法是自定义routing,根据城市ID或者经纬度网格哈希到不同的分片。虽然配置稍微复杂点,但能保证集群负载相对均衡。
别信那些“开箱即用”的神话。elasticsearchnet geo 调优没有银弹,只有不断的压测和参数调整。记住,慢查询日志是你的朋友,开启slowlog,盯着那些超过200毫秒的查询,一个个优化,直到它们消失。这才是正道。
本文关键词:elasticsearchnet geo