MongoDB 7.0起原生支持$median和$percentile,但需版本≥7.0且配置启用;低版本或部分托管服务(如早期Atlas免费层)不支持,可用$sort+$group+$arrayElemAt手动实现中位数计算。

MongoDB 7.0 起已原生支持 $median 和 $percentile,但必须确认你的部署版本和运行模式——低版本或某些托管服务(如早期 Atlas 免费层)可能仍禁用这些运算符。
检查是否可用 $median 和 $percentile
直接在 shell 或 Compass 中运行:
db.collection.aggregate([{
$group: {
_id: null,
med: { $median: "$score" },
p95: { $percentile: { input: "$score", p: [0.95], method: "approximate" } }
}
}])
如果报错 "unknown group operator: $median" 或 "unrecognized expression '$percentile'",说明当前环境不支持——不是语法写错了,是版本/配置限制。
- 确认 MongoDB 版本:
db.version()必须 ≥ 7.0 -
$median仅在$group阶段作为累加器使用,不能用于$project -
$percentile的method参数只能是"approximate",不支持"exact" - Atlas 用户需检查集群版本及是否启用「Advanced Features」(部分共享版默认关闭)
用 $sort+$group+$arrayElemAt 手动算中位数(兼容所有版本)
核心逻辑是:先排序 → 聚合成数组 → 按索引取中间值。注意两点:数组索引从 0 开始;偶数长度要取两个中间值平均。
典型管道片段:
[{
$sort: { score: 1 }
}, {
$group: {
_id: null,
scores: { $push: "$score" }
}
}, {
$project: {
median: {
$cond: {
if: { $eq: [{ $mod: [{ $size: "$scores" }, 2] }, 1] },
then: { $arrayElemAt: ["$scores", { $floor: { $divide: [{ $size: "$scores" }, 2] } }] },
else: {
$divide: [
{ $add: [
{ $arrayElemAt: ["$scores", { $subtract: [{ $divide: [{ $size: "$scores" }, 2] }, 1] }] },
{ $arrayElemAt: ["$scores", { $divide: [{ $size: "$scores" }, 2] }] }
]
},
2
]
}
}
}
}
}]
- 必须带
$sort,否则$push不保证顺序 - 大数据量(>10 万文档)易触发
"Sort exceeded memory limit",需加allowDiskUse: true - 若只关心单个字段中位数,且集合有索引(如
{score: 1}),$sort阶段可利用索引加速
计算 P95、P99 等百分位数的两种方式
推荐优先用原生 $percentile,它基于 t-digest 算法,内存友好且支持流式处理;退而求其次才用数组模拟。
原生写法(支持多值一次算):
{ $percentile: { input: "$response_time", p: [0.5, 0.95, 0.99], method: "approximate" } }
返回结果为数组:[med, p95, p99],顺序与 p 中一致。
- 输入字段含非数值(如
null、字符串)会被自动忽略 - 近似误差通常
- 手动模拟 P95 需算索引
{$floor: {$multiply: [{$size: "$arr"}, 0.95]}},还要防越界({$min: [index, {$subtract: [{$size: "$arr"}, 1]}]}),代码冗长且易错
生产环境关键注意事项
别在高并发实时查询里反复跑全量中位数聚合。真实系统中,这几个点最容易被跳过:
-
$percentile和$median在分片集群上会把数据拉到主分片再计算,跨分片时延迟明显升高 - 即使版本支持,Aggregation Pipeline 的
maxTimeMS仍可能中断长耗时计算,建议预聚合到统计表 - 如果字段基数极高(如用户 ID),
$push生成的数组可能超 BSON 16MB 限制,直接失败 - 应用层计算更可控:导出抽样数据,在 Python/Node.js 里用
numpy.percentile或quantile库算,再写回 DB


















