$sort阶段应紧随$match之后以提升性能,避免放于管道开头或$limit之后;多字段排序需补唯一字段如_id确保稳定性;字符串数字排序须启用collation并配对应索引。

$sort 阶段必须放在聚合管道靠前但非最前的位置
直接把 $sort 放在第一个阶段,看起来顺理成章,但实际会拖慢整个管道。MongoDB 的 $sort 是阻塞式阶段 —— 它必须等上游所有文档都进来才开始排序,如果上游没做过滤,就得对全集合扫描再排序,内存暴涨、响应变慢。
- 优先用
$match过滤掉无关数据,再进$sort;比如查“状态为 active 且金额 > 1000 的订单”,先{$match: {status: "active", amount: {$gt: 1000}}},再{$sort: {amount: -1}} - 如果后续还有
$group或$project,且不需要原始顺序,$sort尽量紧贴$match后面,避免在变换后的大体积文档上排序 - 注意:不能把
$sort放在$limit后面——那样只对前 N 条排序,结果错误
多字段排序时字段顺序和唯一性决定结果稳定性
写 {$sort: {status: 1, amount: -1}} 没问题,但若大量文档 status 值相同(比如全是 "pending"),仅靠 amount 排序无法保证每次查询返回顺序一致 —— MongoDB 不保证等值键文档的相对顺序。
- 解决办法是补一个唯一字段,最常用的是
_id:{$sort: {status: 1, amount: -1, _id: 1}} - 如果业务上有天然唯一字段(如
order_no),优先用它替代_id,语义更清晰 - 不要重复写同一字段,如
{amount: 1, amount: -1}会报错;也不能超过 32 个排序键
字符串数字排序需显式启用 collation
当字段存的是字符串格式的数字(如 "10"、"2"),默认按字典序排,结果是 "10", "2", "20",而非数值序 "2", "10", "20"。
- 必须加
.collation({locale: "en", numericOrdering: true}),且该调用要放在aggregate()之后、执行之前 - 注意:collation 对索引有要求 —— 对应字段的索引也得建在相同 collation 下,否则无法命中索引,排序退化为内存排序
- PyMongo 中写法示例:
collection.aggregate(pipeline).collation({"locale": "en", "numericOrdering": True})
排序性能瓶颈几乎总是索引缺失或不匹配
即使写了 $sort,没有对应索引,MongoDB 就只能走内存排序(in-memory sort)。一旦数据量超过 100MB,就可能触发 errmsg: "Sort exceeded memory limit..." 错误。
- 索引字段顺序必须和
$sort字段顺序严格一致,方向尽量一致(升序索引可服务升序/降序查询,但混合方向时建议建双向索引) - 复合排序
{a: 1, b: -1}要配索引{a: 1, b: -1};若只有{a: 1}索引,则b部分无法利用索引 - 用
explain("executionStats")检查是否用了索引:看executionStats.totalDocsExamined是否接近nReturned,以及stage是否为IXSCAN
真正卡住人的往往不是语法写错,而是排序字段没索引、等值字段没加 _id、字符串数字忘了 collation —— 这三处一漏,结果不准或直接失败。

















