MongoDB 中的排序文档是传递给 sort 方法的对象,用来定义查询结果按哪些字段、以什么方向排列。它看似只有 1 和 -1 两个取值,却直接决定了查询过程是否走索引、是否触发内存排序,以及多字段业务排序是否准确。很多性能波动和排序错乱问题,追查到最后都会落到这个小小的排序参数上。下面先从语法和执行规则入手,再结合执行计划与常见误区,把 MongoDB 排序文档一次说清。

一、排序文档的基本语法与多字段优先级
MongoDB 的 sort 方法接收一个文档作为排序规范,文档中的键是字段名,值必须是数值 1 或 -1。1 表示升序,数值小的排在前面;-1 表示降序,数值大的排在前面。以订单集合为例,如果需要按创建时间倒序返回已支付订单,可以这样写:
db.orders.find({ status: "paid" }).sort({ createTime: -1 });
当排序文档中出现多个字段时,MongoDB 会严格按照字段的书写顺序执行排序。也就是说,先按第一个字段排序,只有第一个字段值相同的文档才会进入第二个字段的比较。比如排序文档 { createTime: -1, _id: -1 } 表示优先按创建时间倒序,创建时间相同的记录再按 _id 倒序。这个顺序并不会被数据库优化器重新编排,因此业务上哪个字段优先级高,就必须把它写在排序文档的第一位。
排序文档还可以作用于日期、字符串、数值、数组等不同类型的字段。字符串字段的默认排序规则基于二进制比较,对于中文或大小写混合内容可能不符合自然语言习惯,此时可以通过 collation 指定排序规则。无论字段类型如何,排序值本身只接受数值 1 和 -1,任何字符串、布尔值或对象写法都会导致排序行为不符合预期。
二、排序文档与索引的配合机制
排序文档不仅是结果顺序的声明,更是查询执行计划的重要输入。MongoDB 执行排序有两种方式:一种是从索引中直接读取已经排好序的数据,另一种是先读取数据,再在内存中创建临时集合进行排序。前者几乎不消耗额外内存和 CPU,后者则受限于 32MB 内存上限,当排序数据量较大时会抛出 Sort operation used more than the maximum 33554432 bytes of RAM 错误。要避免这个问题,核心思路就是让排序字段命中合适的索引。
对于单字段排序,使用该字段的索引即可,无论索引方向是升序还是降序,MongoDB 都可以通过正向或反向扫描索引来获得对应顺序。例如索引 { createTime: 1 } 既可以高效支持 sort({ createTime: 1 }),也可以支持 sort({ createTime: -1 })。但对于复合排序和复合索引,方向匹配就变得严格:排序文档的字段顺序必须与复合索引前缀一致,同时每个字段的排序方向要与索引定义完全相同或完全相反。
假设业务经常执行 find({ status: "paid" }).sort({ createTime: -1, _id: -1 }),可以创建索引 { status: 1, createTime: -1, _id: -1 }。查询条件先等值过滤 status,排序字段 createTime 和 _id 与索引后缀方向一致,MongoDB 就能在索引范围内直接按顺序返回结果。通过 explain("executionStats") 查看执行计划时,如果 winningPlan 中没有单独的 SORT 阶段,说明排序已经由索引覆盖;如果出现 SORT 阶段,则应检查排序文档与索引定义是否匹配。
三、常见误区与避坑建议
第一个误区是把排序值写成字符串 "asc"、"desc" 或 "1"、"-1"。MongoDB 排序文档只认可数值 1 和 -1,字符串值不会被解释为升降序方向。实际执行时,这种错误可能导致查询直接报错,也可能被某些驱动转换为不可预期的行为。写排序参数时务必保持数值类型,不要从配置文件中读取字符串后直接传给 sort。
第二个误区是忽略多字段排序的书写顺序。有些开发者认为数据库会自动选择最优排序顺序,于是随意书写字段,但 MongoDB 不会重排排序文档中的键。先写 { createTime: -1, _id: -1 } 和先写 { _id: -1, createTime: -1 } 得到的结果完全不同,前者才是真正按时间优先的业务逻辑。尤其是分页查询中,排序文档必须与偏移字段联合设计,否则容易出现重复或漏数据。
第三个误区是认为只要某个字段存在索引,排序就一定能走索引。事实上,只有当排序字段构成某个复合索引的前缀,并且方向匹配时,索引才能完全覆盖排序。举个例子,索引 { status: 1, createTime: -1 } 可以覆盖 sort({ status: 1, createTime: -1 }),但如果排序文档写成 { createTime: -1, status: 1 },索引顺序与排序要求不一致,执行计划中就可能出现额外的内存排序。
第四个误区是忽视 sort 与 limit 的组合效果。在无索引排序中,MongoDB 必须扫描并排序全部匹配文档后,才能返回前 N 条;而在索引排序中,扫描到 N 条结果即可提前终止。因此即使业务只取前 10 条,若排序文档没有索引支撑,性能开销也可能与全量排序相当。对高频分页查询、最近消息列表、热门订单列表等场景,应优先为过滤条件和排序字段设计复合索引。
最后还要注意,聚合管道中的 $sort 阶段虽然也使用 1 和 -1,但它属于聚合表达式,可以引用计算字段或管道中新增字段;而普通查询中的 sort 方法只能针对文档原始字段排序,不能使用表达式。两者不要混用,遇到复杂排序逻辑时应先通过聚合生成排序字段,再执行 $sort。
把排序文档当成单纯的参数容易忽略它背后的执行机制,真正可靠的写法是:明确业务排序优先级,保持数值类型的排序值,并让排序字段与复合索引前缀和方向一致。这样既能保证结果顺序准确,也能避免 32MB 内存排序限制带来的生产问题。
MongoDB排序文档排序性能复合索引修改时间:2026-08-28 22:21:49