$group不能实现负载均衡,它仅聚合已送达数据;真正负载均衡由mongos路由、分片键设计和readPreference控制。

直接用 $group 做“负载均衡统计”是个常见误解——它本身不参与请求分发或路由,只负责聚合已有数据。如果你真想统计各客户端/服务来源的请求量(比如用于事后分析流量分布),$group 是对的工具;但若指望它让 MongoDB 自动把请求打散到不同节点,那它完全做不到。
为什么 $group 不能实现真正的负载均衡
$group 是聚合管道中的一个阶段,运行在 mongos 或单节点上,只处理已送达的数据流。它不干预网络层、不修改查询路由逻辑、也不影响 mongos 如何将请求派发到后端分片。所谓“负载均衡”,在 MongoDB 里由三层机制承担:mongos 的查询路由、分片键设计决定的数据分布、副本集的读偏好(readPreference)控制读流量走向。
你看到的“按来源统计”,只是把日志或埋点集合里记录的 source_ip、service_name 等字段做归类计数,属于事后分析,和实时负载调度无关。
用 $group 统计请求来源的正确写法
假设你有一个 access_log 集合,每条文档含 client_id、timestamp、endpoint 字段,想看最近一小时各 client_id 的调用次数:
db.access_log.aggregate([
{ $match: { timestamp: { $gte: new Date(Date.now() - 60 * 60 * 1000) } } },
{ $group: { _id: "$client_id", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
])-
_id: "$client_id"是分组键,必须存在且不能省略 - 如果想按多个字段组合分组,写成
_id: { client: "$client_id", env: "$env" } - 避免在
$group前漏掉$match:没范围过滤就全表扫描,对大集合极伤性能 - 注意
client_id字段要有索引,否则$match阶段就慢
容易被忽略的坑:$group 输出的 _id 类型影响后续处理
$group 输出的 _id 字段类型取决于你的表达式。比如:
-
_id: "$client_id"→_id类型和原始字段一致(可能是字符串、ObjectId、数字) -
_id: { $toString: "$client_id" }→ 强制转为字符串,方便下游系统统一解析 -
_id: null→ 所有文档聚成一条,_id: null,常用于全局统计总数,但容易误用成“不分组”
尤其当 client_id 是 ObjectId 时,某些监控系统或 BI 工具可能无法直接渲染 _id 字段,这时显式转成字符串更稳妥。
真正影响负载均衡的关键配置不在 $group 里
如果你实际目标是让流量更均匀地落到各个副本集节点或分片上,该检查的是:
- 应用层连接字符串是否设置了
readPreference=secondaryPreferred或nearest - 分片键是否具备高基数、低热度(避免所有写入集中到一个 chunk)
- mongos 连接池是否配置了足够多的连接(
maxPoolSize),防止连接复用瓶颈 - 是否有大量未命中索引的查询导致某个分片 CPU 持续飙升(这时
$group统计再准也救不了)
换句话说:$group 能告诉你“谁打得多”,但没法帮你“把流量导走”。要导流,得动连接配置、分片键、读偏好,而不是改聚合语句。


















