$in 里放多少个值合适?对索引查询的耗时、规划时间和结果顺序有什么影响?这篇把元素数量从 1 逐步增大到 100,000 测一遍,还试了无索引、与 $or 对比和大小上限。
S 实际去重后 耗时 每个值 返回 检查的键
1 1 0.2 ms 237.5 μs 1 1
10 10 0.2 ms 19.1 μs 10 20
100 100 0.3 ms 3.4 μs 100 200
1,000 1,000 2.2 ms 2.2 μs 1,000 1,996
10,000 9,903 17 ms 1.7 μs 9,903 19,604
100,000 90,619 126 ms 1.4 μs 90,619 164,829固定开销约 0.2 ms,之后每个值约 1.4~2 μs:1,000 个 2.2 ms,10 万个 126 ms。
S= 1:optimizationTimeMicros = 130 S= 100:optimizationTimeMicros = 98 S= 10,000:optimizationTimeMicros = 478 S=100,000:optimizationTimeMicros = 4428
$in 小于一百个时规划不到 0.2 ms,10 万个时规划本身要 4.4 ms。
test> db.t.find({k: {$in: [300, 3, 150, 3]}}) // 列表顺序 300, 3, 150, 3
[ 3, 150, 300 ]
# indexBounds:排序、去重后的单点区间
test> var e = db.t.find({k: {$in: [300, 3, 150, 3]}}).explain(); var s = e.queryPlanner.winningPlan; while (s.inputStage) s = s.inputStage; JSON.stringify(s.indexBounds)
{"k":["[3, 3]","[150, 150]","[300, 300]"]}结果按索引键顺序返回,不是 $in 列表的顺序;重复的值只匹配一次。
S= 10: 79 ms(COLLSCAN 500,000 个文档) S= 1,000: 97 ms(COLLSCAN 500,000 个文档) S=100,000: 200 ms(COLLSCAN 500,000 个文档)
全表扫描时元素越多越慢但增长平缓:50 万个文档上 S=10 用 79 ms,S=100,000 用 200 ms。
$in 1.9 ms;$or 1000 个分支 2.2 ms
同样的 1000 个值,$in 与 1000 个 $or 分支耗时接近(1.9 与 2.2 ms)。
150 万个整数(BSON 数组 18,388,903 字节)的 $in: DocumentTooLarge: BSON document too large (18388999 bytes) - the connected server supports BSON document sizes up to 16777216 bytes. 100 万个整数(BSON 数组 11,888,903 字节)的 $in:成功,返回 333,334 个,耗时 477 ms
整条命令受 16 MB 上限约束:100 万个整数的 $in(11.9 MB)能执行,150 万个(18.4 MB)被拒绝。