MongoDB从4.2版本开始,在聚合管道中引入了$serverStatus阶段,它允许开发者在不退出聚合框架的前提下,直接读取数据库实例的运行状态。传统做法是通过命令行执行db.serverStatus()获取一整份庞大的JSON,再在应用层解析;而$serverStatus作为管道的第一个阶段,能把服务器状态变成一条普通文档,从而无缝衔接后续的转换、过滤与关联操作。这种方式特别适合需要把实例健康度与业务集合数据联合分析的场合,例如统计每秒写入量与当前打开连接数的比值。

一、$serverStatus阶段的基础用法与字段结构
在聚合管道中使用$serverStatus非常简单,只需要把它放在管道数组的第一位即可。它不需要任何参数,执行后会生成一条包含host、version、uptime、connections、mem、opcounters等顶层字段的文档。其中connections字段描述当前连接情况,mem反映内存使用,opcounters则记录增删改查等各类操作的累计次数。由于该阶段输出的是单文档,如果管道中只有它,查询结果就是这一条状态记录。
我们可以通过$project阶段对返回字段进行精简。例如只保留connections.current和opcounters.insert,避免拿到几百行无用指标。下面的例子展示了如何获取当前连接数与插入操作总数:
db.aggregate([
{ $serverStatus: { } },
{ $project: {
host: 1,
currentConn: "$connections.current",
totalInsert: "$opcounters.insert",
_id: 0
} }
]);
需要注意的是,$serverStatus返回的字段名中存在嵌套结构,引用时必须使用点号路径。另外,该阶段消耗的资源极低,因为它读取的是mongod内部已维护的计数器,不会触发额外扫描。不过在分片集群上,它只反映执行该管道的mongos或mongod本地状态,要获取全局需逐节点执行。
二、将服务器状态与业务集合数据关联分析
单纯查看服务器状态往往不够直观,真正有价值的是把状态与业务表数据放在同一个分析视角下。由于$serverStatus产生的是单文档,我们可以使用$unionWith或者先查出状态再$lookup的方式,把它和业务集合的聚合结果合并。比如先统计订单集合在过去一分钟的写入量,再取出实例的当前连接数,从而计算单连接平均写入压力。
下面的代码演示了先通过$serverStatus取状态,再利用$lookup关联一个预先算好的业务统计集合。业务统计集合business_stats中存放了每分钟的写入数,我们通过匹配最近一条记录来对比:
db.aggregate([
{ $serverStatus: { } },
{ $project: {
currentConn: "$connections.current",
dirty: "$wiredTiger.cache.bytes currently in the cache",
_id: 0
} },
{ $lookup: {
from: "business_stats",
pipeline: [
{ $sort: { minute: -1 } },
{ $limit: 1 }
],
as: "recent_biz"
} },
{ $project: {
currentConn: 1,
writePerMin: { $arrayElemAt: [ "$recent_biz.write_count", 0 ] },
connWriteRatio: {
$cond: [
{ $gt: [ "$currentConn", 0 ] },
{ $divide: [ { $arrayElemAt: [ "$recent_biz.write_count", 0 ] }, "$currentConn" ] },
null
]
}
} }
]);
这种写法的优势在于分析逻辑完全下沉到数据库内,应用服务器只需一次请求就能拿到综合结论。相较于把db.serverStatus()和find聚合分开调用再在内存中拼装,减少了网络往返和序列化开销。当然,如果business_stats本身很大,$lookup的子管道也要注意建索引,否则可能抵消掉$serverStatus带来的轻量优势。
三、基于$serverStatus构建轻量监控与告警管道
很多中小团队没有引入完整的监控体系,此时可以用$serverStatus配合$expr和$match做简单的阈值告警。思路是在管道中先计算派生指标,再用$match过滤出异常记录,若返回非空结果则说明触发了条件。例如当打开连接数超过五百且脏页缓存超过两GB时,输出告警信息。
以下示例把状态文档转换后只保留超标项,运维脚本定时运行该聚合,根据是否返回文档来判断健康度:
db.aggregate([
{ $serverStatus: { } },
{ $project: {
conn: "$connections.current",
dirtyBytes: "$wiredTiger.cache.tracked dirty bytes in the cache",
_id: 0
} },
{ $match: {
$expr: {
$and: [
{ $gt: [ "$conn", 500 ] },
{ $gt: [ "$dirtyBytes", 2147483648 ] }
]
}
} },
{ $project: {
alert: "connection and dirty cache high",
conn: 1,
dirtyMB: { $divide: [ "$dirtyBytes", 1048576 ] }
} }
]);
该方案比部署外部采集器更轻便,尤其适合临时排查。但需注意,$serverStatus读取的是进程瞬时值,若采样间隔过长可能漏掉毛刺。建议与慢查询、系统层监控互补使用。另外在MongoDB Atlas等托管服务中,部分字段可能因租户隔离不可见,编写管道时要先做字段存在性判断,避免意外报错。
通过上面的讨论可以看到,$serverStatus不仅是一个状态查看器,更是聚合管道里的数据源。合理组合投影、关联与过滤,能用很少的代码实现实用的数据库自监控,降低对外部组件的依赖。
MongoDB聚合管道serverStatus修改时间:2026-08-18 14:44:28