本文关键词:es geo搜索原理
上周有个兄弟半夜给我打电话,哭爹喊娘说ES集群崩了。查了半天日志,发现是个刚入行的小伙子,搞了个“附近的人”功能,结果一跑查询,CPU直接飙到100%,整个集群卡成PPT。我一看他的查询语句,差点没把隔夜饭吐出来。这哥们儿居然在Geo字段上用了默认的text类型去模糊匹配,还搞了个全表扫描的range查询。我说你这是在跟服务器拼命啊!今天咱就掰扯掰扯这es geo搜索原理,别整那些虚头巴脑的理论,直接上干货,全是血泪教训换来的。
首先你得明白,ES里的地理位置不是简单的数字,它是经过特殊编码的。这就是所谓的geohash或者grid square。很多新人以为把经纬度存进去就能搜,大错特错!如果不指定类型,ES根本不知道这是个地理位置,它就是个普通字符串。所以第一步,建索引的时候,字段类型必须指定为geo_point。这点没得商量,谁偷懒谁后悔。
第二步,理解空间索引。ES底层用的是Lucene,它把地球表面切成了很多小格子。当你查询“半径5公里内的用户”时,ES不是去算每个点的距离,而是先算出这个圆覆盖了多少个格子,然后去这些格子里找数据。这就是es geo搜索原理的核心:先筛选格子,再精确计算。如果你数据量百万级,这速度那是嗖嗖的;要是你没建对索引,或者用了错误的查询方式,那就像在图书馆里找一本书,却不看索引,直接一页页翻,能不慢吗?
再说说那个让人头秃的精度问题。我见过太多人,为了追求极致精度,把经纬度保留到小数点后8位。其实没必要!对于大多数业务场景,比如外卖配送、打车定位,小数点后5位就够了,精度大概在1米左右。你非要存8位,不仅浪费存储空间,还会让索引变大,查询变慢。数据对比很直观:小数点后5位,索引大小能节省大概30%的查询响应时间。这笔账,算清楚没?
还有个大坑,就是geo_shape。有些兄弟做复杂的多边形查询,比如“查询在某个商圈内的所有店铺”,这时候geo_point就不够用了,得用geo_shape。但要注意,geo_shape的查询性能比geo_point差不少,因为它涉及到更复杂的几何运算。如果你的业务只是简单的圆形范围查询,千万别用geo_shape,纯属给自己找罪受。我之前有个项目,为了炫技用了geo_shape,结果上线后QPS根本提不上去,最后不得不重构,改回geo_point加预计算的方式,才把性能救回来。
具体怎么操作?第一步,确认业务需求。是圆形搜索还是多边形?圆形用geo_point,多边形用geo_shape。第二步,建索引。geo_point字段要开启doc_values,这样聚合查询才快。第三步,写查询。用geo_distance查询圆形,用geo_bounding_box查矩形。别用match_all去扫,那是自杀行为。
最后提醒一句,ES的版本升级很快,不同版本的geo查询优化策略可能不一样。比如7.x版本对geohash grid做了优化,查询速度比6.x快了不少。所以,别老盯着旧文档看,多看看官方最新的release notes。
总之,搞es geo搜索原理,核心就是“先粗筛,后精算”。别试图用暴力计算去对抗物理规律,那是徒劳的。按照我说的步骤去调优,保证你的查询速度快到飞起。要是还遇到问题,欢迎留言,咱一起聊聊,毕竟这行水深,多个人多双眼睛,总能避开几个坑。记住,代码是写给人看的,顺便给机器执行,别整那些花里胡哨的,实用才是王道。