说实话,刚接触 geo mongodb 的时候,我真是又爱又恨。爱的是它处理地理位置数据确实快,恨的是那个文档结构,稍微不注意就让你查不出数据或者慢得想砸键盘。今天我不讲那些虚头巴脑的理论,就讲讲我自己在项目里真金白银砸出来的教训。你要是还在用老办法搞空间查询,赶紧停下来看看。
先说个惨痛的经历。去年我们接了个外卖配送范围的项目,老板说要用 geo mongodb 实现“附近的人”功能。我心想这有啥难的,MongoDB 原生支持 GeoJSON 啊。结果呢?上线第一天,服务器直接爆满。为什么?因为我建索引建错了。很多人不知道,geo mongodb 的空间索引对数据格式要求极其严格。你必须把坐标封装成标准的 GeoJSON 对象,而且字段名必须叫 coordinates,不能叫 loc,也不能叫 point。我当时偷懒,直接存了个数组 [116.40, 39.90],结果查询的时候,MongoDB 根本识别不了,直接全表扫描。那速度,慢得像蜗牛爬。后来我花了三天时间重构代码,把所有数据清洗了一遍,改成标准的 GeoJSON 格式,查询速度瞬间从 2 秒降到了 20 毫秒。这教训,血泪般的。
再说说价格问题。很多人觉得 MongoDB 开源免费,用着没成本。错!当你数据量上千万,而且频繁做 geo mongodb 空间查询时,内存和 CPU 的开销是巨大的。我测试过,同样的数据量,用错误的索引方式,内存占用是正确方式的 3 倍。如果你用的是云数据库,那流量费和计算费能让你怀疑人生。所以,别省那点开发时间,前期把 geo mongodb 的数据模型设计好,后期能省下一大笔服务器费用。
还有,别迷信 $near 查询。虽然它好用,但在大数据量下,它的性能并不稳定。我推荐大家用 $geoWithin 配合 $box 或者 $polygon。比如,你要查一个矩形区域内的所有用户,用 $geoWithin 比 $near 快得多。为什么?因为 $near 需要计算距离并排序,而 $geoWithin 只是判断点是否在多边形内,不涉及排序。这个细节,很多教程里都没提,但我在实际生产环境中对比过,数据量超过 100 万时,$geoWithin 的速度优势非常明显。
另外,还有一个大坑,就是坐标系的混淆。geo mongodb 默认支持 WGS84 坐标系,也就是 GPS 坐标。但有些第三方地图服务返回的是 GCJ-02 或者 BD-09 坐标系。如果你直接把这些坐标存进 MongoDB,查出来的位置全是错的。我见过太多人栽在这个坑里。解决办法很简单,在存入数据库之前,先用代码把坐标转换一下。别嫌麻烦,否则后期排查问题,能让你怀疑人生。
最后,给个实用的步骤,照着做能避坑:
第一步,确定你的业务场景。是查附近的人,还是查某个区域内的数据?如果是前者,用 $near;如果是后者,用 $geoWithin。
第二步,设计数据模型。确保你的坐标字段是 GeoJSON 格式,字段名是 coordinates,类型是数组,顺序是 [经度, 纬度]。别搞反了,MongoDB 对顺序很敏感。
第三步,创建索引。用 2dsphere 索引,别用 2d 索引,除非你确定你的数据是平面坐标。2dsphere 支持球面几何,更准确。
第四步,测试性能。用 explain() 查看查询计划,确保用到了索引。如果没用到,检查你的数据格式和索引类型。
第五步,监控线上性能。上线后,实时监控慢查询日志。一旦发现慢查询,立即优化。
记住,geo mongodb 不是万能的,但它确实强大。用对了,事半功倍;用错了,后悔莫及。希望我的这些经验,能帮你少走弯路。毕竟,时间就是金钱,不是吗?