地理空间查询慢主因是索引类型、精度、写法或数据分布问题,应先用explain()分析执行计划;2dsphere因球面计算开销大、筛选效率低导致慢,而2d仅平面距离更轻量;多边形复杂、坐标不规范、分片策略不当等均会触发全表扫描或性能断崖。

地理空间索引查询慢,大概率不是“没建索引”,而是索引类型、字段精度、查询写法或数据分布出了问题。直接上 explain() 看执行计划,比盲目重建索引更有效。
为什么 2dsphere 索引查得慢,但 2d 却快?
2dsphere 支持球面几何(经纬度真实距离计算),2d 仅支持平面欧氏距离;前者在高纬度或跨经度区域会触发大量候选点过滤,尤其当 $geoWithin 或 $near 范围过大时,keysExamined 可能远高于 docsExamined —— 这说明索引扫描效率低,不是没走索引,而是索引“筛得不干净”。
- 用
db.collection.explain("executionStats").find({loc: {$near: { $geometry: {type:"Point", coordinates:[116.4,39.9]}, $maxDistance: 1000}}})查看executionStats中的keysExamined和docsExamined比值;若前者是后者的 5 倍以上,说明索引选择性差 -
$near必须配合sort()使用,且不能和$where、$text等非索引操作混用,否则退化为内存排序 - 如果业务只做城市内小范围检索(如 5km),且坐标精度不高(保留 4~5 位小数),可考虑改用
2d+ 平面距离公式(如 Haversine 近似),避免2dsphere的球面三角开销
$geoWithin 和 $geoIntersects 为何常触发 COLLSCAN?
这两个操作符对多边形复杂度敏感。MongoDB 在预估多边形覆盖面积时,若顶点数超阈值(默认约 1024),或存在自相交、极小环、跨国际日期变更线等异常结构,会放弃使用索引,直接回退到全集合扫描。
- 用
db.collection.find({loc: {$geoWithin: {$geometry: {...}}}}).explain("queryPlanner")确认winningPlan.stage是否为GEO_NEAR_2DSPHERE或IXSCAN;若是COLLSCAN,重点检查多边形 GeoJSON 格式 - 简化多边形:用
turf.simplify()或 PostGIS 的ST_SimplifyPreserveTopology预处理,控制顶点 ≤ 500 - 避免用
$geoIntersects查询点是否在面内——该操作符设计用于面-面关系;点查面请用$geoWithin
坐标字段嵌套太深或类型不一致导致索引失效
MongoDB 地理空间索引只认顶层字段或明确路径的子字段,且要求值严格为 {type: "Point", coordinates: [lon, lat]} 结构。任何偏差都会让索引完全不命中。
- 确认字段路径:索引建在
"loc",但文档里存的是{"address": {"geo": {...}}}→ 必须建在"address.geo",且查询也要写成{'address.geo': {...}} - 检查坐标顺序:必须是
[经度, 纬度](注意不是 lat, lon);反序会导致索引无法匹配,explain中indexBounds为空 - 排查空值/非法值:
null、undefined、coordinates是字符串或数组长度 ≠ 2 的文档,会被索引跳过;可用db.collection.countDocuments({"loc.coordinates.0": {$exists: true, $type: "number"}})校验数据质量
分片集群下地理查询性能断崖式下降
地理空间查询在分片集群中无法下推到所有分片并行执行,尤其是 $near 类操作必须由 mongos 合并结果再排序,网络传输和内存排序开销剧增。
-
$near查询强制要求_id字段存在,否则 mongos 无法去重合并;缺失_id会报错或返回乱序结果 - 避免在未以地理字段分片的集合上做全局
$near;应优先考虑按区域哈希分片(如{region: "hashed"}),再结合应用层路由缩小查询范围 - 用
db.adminCommand({splitVector: "db.col", keyPattern: {loc: "2dsphere"}, maxChunkSizeBytes: 10485760})检查分片键与地理索引是否冲突;若分片键不含地理字段,mongos 会广播查询到所有分片
真正卡住的往往不是“要不要建索引”,而是坐标格式是否合规、多边形是否可索引、分片策略是否让地理查询变成单点瓶颈——这些细节在 explain("executionStats") 里都有迹可循,但容易被忽略。

















