排序数据超过 100MB 会怎样?这篇用 150 MB 的集合在 allowDiskUseByDefault 开关两种状态下对比,再看 limit 和索引如何避开这个限制。
test> db.adminCommand({getParameter: 1, allowDiskUseByDefault: 1, internalQueryMaxBlockingSortMemoryUsageBytes: 1})
{
allowDiskUseByDefault: true,
internalQueryMaxBlockingSortMemoryUsageBytes: Long('104857600'),
ok: 1
}
test> db.big.stats().size / 1024 / 1024 + " MB(big 的 dataSize)"
147.48573303222656 MB(big 的 dataSize)阻塞排序(无索引可用的 SORT 阶段)最多用 100 MiB 内存。9.0.2 里 allowDiskUseByDefault 默认为 true,超过就自动溢出磁盘。
test> db.adminCommand({setParameter: 1, allowDiskUseByDefault: false})
{ was: true, ok: 1 }
test> db.big.find().sort({k: 1})
{
code: 292,
message: 'Executor error during find command: test.big :: caused by :: Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting.'
}
test> db.small.find().sort({k: 1}) // 50 MB,没超
50000关掉 allowDiskUseByDefault(旧版本的默认行为)后,超过上限报 code 292 did not opt in to external sorting,没超的小集合排序正常。
test> db.big.find().sort({k: 1}).allowDiskUse().explain("executionStats")
{
nReturned: 150000,
ms: 337,
stages: [
{
stage: 'SORT',
usedDisk: true,
spills: 2,
spilledBytes: 155400000,
memLimit: 104857600
}
]
}传 allowDiskUse 后排序溢出磁盘:explain 里 SORT 阶段 usedDisk: true、spills: 2。
small(50,000 个):不排序 30 ms;排序 46 ms(内存) big(150,000 个): 不排序 97 ms;排序 144 ms(溢出磁盘,约 150 MB 的排序数据)
150 MB 的溢出磁盘排序比不排序多 47 ms;50 MB 的内存排序多 16 ms。
test> db.big.find().sort({k: 1}).limit(100) // allowDiskUseByDefault 仍是 false
100
test> var e = db.big.find().sort({k: 1}).limit(100).explain("executionStats"); var s = e.executionStats.executionStages; var o = null; while (s) { if (s.stage.includes("SORT")) o = {stage: s.stage, usedDisk: s.usedDisk, limit: s.limitAmount}; s = s.inputStage }; o
{ stage: 'SORT', usedDisk: false, limit: 100 }带 limit 的排序只保留前 N 个,不会超限,usedDisk: false。
test> db.big.find().sort({k: 1}) // 有索引,allowDiskUseByDefault 仍是 false
150000
test> explain
{
plan: 'FETCH > IXSCAN(k_1)',
nReturned: 150000,
keysExamined: 150000,
docsExamined: 150000,
ms: 87
}
有索引的排序读取全部 150,000 个文档(去掉 pad 字段):155 ms(无索引、溢出磁盘:144 ms)
test> db.adminCommand({setParameter: 1, allowDiskUseByDefault: true})
{ was: false, ok: 1 }排序字段有索引时没有 SORT 阶段,不受内存上限影响;不过读取全部文档的总耗时不一定更短(155 ms 对 144 ms)。