说实话,刚接手那个LBS项目的时候,我整个人都是懵的。老板说要做个附近的人功能,我心想这还不简单?建个索引,存个经纬度,搜一下完事。结果呢?第一天上线,数据查出来全是乱的,有的甚至把南极洲的人搜出来了。那几天我头发掉了一把,直到我真正搞透了elasticsearch的geo这块逻辑,才算是把心放回肚子里。今天就把我这些血泪经验写下来,希望能帮兄弟们少熬几个夜。
首先,别一上来就搞什么复杂的自定义脚本,老老实实用geo_point类型。这是elasticsearch的geo最基础也最稳的方案。很多新手喜欢用两个float字段分别存lat和lon,然后自己写查询,那是给自己挖坑。一旦数据量大起来,查询效率低得让你怀疑人生。记住,第一步,建索引的时候,字段类型必须指定为geo_point。
第二步,数据入库的时候,格式一定要对。有两种写法,一种是数组格式[经度,纬度],注意顺序,千万别搞反了,经度在前,纬度在后,这是国际惯例,也是ES的规矩。另一种是对象格式,{"lat": 39.9, "lon": 116.4}。我当初就是栽在数组顺序上,把经纬度写反了,结果搜索范围直接偏移了大半个地球,客户投诉电话被打爆,那滋味真不好受。
第三步,查询的时候,别用match_query,要用geo_distance或者geo_bounding_box。geo_distance适合查“半径内”的人,比如“附近5公里”。geo_bounding_box适合查“矩形区域”的人,比如“朝阳区范围内”。这里有个大坑,就是精度问题。geo_point默认精度只有50米,如果你需要更精确的定位,比如共享单车找车,必须调整index_geohash_precision参数。我在测试环境发现,默认精度下,两辆车停得挺近,但搜不出来,调高精度后立马正常。
再说说性能优化。很多兄弟问,数据量大了怎么搞?别慌,分片要设好。geo查询对分片比较敏感,建议根据业务场景设置合理的分片数。另外,缓存很重要。如果热点区域多,开启查询缓存能显著提升响应速度。我有个案例,某外卖平台,通过优化geo查询缓存,QPS从2000提升到了8000,服务器压力骤减。
还有,别忽视边界情况。比如跨越国际日期变更线,或者极地地区,普通的矩形框查询可能会失效。这时候得用geo_shape,虽然复杂点,但能处理更复杂的地理形状。不过对于大多数“附近的人”场景,geo_point足够了。
最后,提醒一点,别迷信“最新”的教程。ES版本迭代快,有些老教程里的API在新版本里已经废弃了。一定要看官方文档,而且要看对应版本的文档。我当初照着三年前的博客搞,结果API报错,查了半天才发现是版本不兼容。
总结一下,elasticsearch的geo定位其实不难,难的是细节。经纬度顺序、精度设置、查询方式选择,每一个环节都不能马虎。我花了半个月时间踩坑,现在回头看,其实就那几行代码的事。希望兄弟们能避开我走过的弯路,早点下班回家陪陪家人。毕竟,代码是写不完的,生活才是自己的。
本文关键词:elasticsearch的geo