说实话,搞了十三年Geo这行,见过太多因为一个MongoDB查询慢到让业务方拍桌子的案例了。昨天凌晨三点,线上报警群炸了,一个核心LBS模块的接口响应时间飙到了8秒,直接导致APP端用户刷不出附近的商家。运维大哥急得满头大汗,我也被拉起来背锅。查了半天日志,发现罪魁祸首就是那个看似人畜无害的geo查询。今天不整那些虚头巴脑的理论,就聊聊这次mongo geo慢查是怎么被我们一点点揪出来,又是怎么治好的。
很多兄弟一遇到查询慢,第一反应就是加索引,甚至不管三七二十一,给字段全加上2dsphere索引。大错特错!这次事故里,我们最初就是盲目加了索引,结果内存占用飙升,反而拖累了整体性能。真正的病灶在于,我们的数据模型里,有一个字段既存了经纬度,又存了其他业务属性,而且查询条件里还夹杂了非空间维度的过滤。
咱们先说索引。MongoDB的空间索引,2dsphere是必须的,这点没得跑。但是,如果你查询的时候,除了地理位置,还要过滤“状态=1”或者“类别=餐饮”,这时候如果你只用一个复合索引,或者把空间索引和普通字段索引搞混了,MongoDB的查询优化器可能会懵圈。它不知道先过滤空间范围还是先过滤业务字段,导致全表扫描或者索引失效。这就是典型的mongo geo慢查场景。
我当时的排查思路是这样的。首先,用explain()看执行计划。这一看不要紧,好家伙,stage是COLLSCAN,也就是集合扫描,这意味着索引根本没生效,或者优先级极低。这说明什么?说明你的查询条件写得太复杂,或者索引结构不对。
然后,我检查了数据分布。发现有些商家的经纬度精度不够,或者有些脏数据把经纬度写成了字符串,而不是数组格式。这种脏数据在空间索引里是没法被高效检索的,每次查询都要做类型转换,CPU直接打满。
解决办法其实挺简单的,但得细心。第一,清洗数据,确保所有经纬度字段都是标准的[经度, 纬度]数组格式,且类型为Number。第二,重建索引。不要搞什么花里胡哨的复合索引,先把空间索引单独建好,确保2dsphere生效。对于非空间维度的过滤,比如“状态”,可以考虑在应用层先做初步过滤,或者使用TTL索引如果数据有有效期的话。
另外,还有一个容易被忽视的点,就是查询范围。如果你查的是“方圆100公里”,这个范围太大,涉及的文档数量惊人,再好的索引也扛不住。这时候,可以考虑分片,或者限制返回数量。我们这次就是限制了返回最多50条结果,并且只查“状态=1”的商家,查询速度瞬间从8秒降到了200毫秒。
最后,提醒一下大家,别迷信“万能索引”。mongo geo慢查很多时候不是索引的问题,而是查询逻辑和数据模型的问题。一定要结合业务场景,看看能不能简化查询条件,或者在写入时就做好数据预处理。
这次事故让我明白,Geo查询不是简单的“查附近的人”,它涉及到数据一致性、索引策略、查询优化等多个方面。希望大家别再踩这种低级错误了。如果你也在为mongo geo慢查头疼,不妨从explain()开始,一步步排查,别急着加索引,先看看数据本身有没有问题。
本文关键词:mongo geo慢查