bigywater.tech
MongoDB · 46

$in 的元素数量对查询的影响

$in 里放多少个值合适?对索引查询的耗时、规划时间和结果顺序有什么影响?这篇把元素数量从 1 逐步增大到 100,000 测一遍,还试了无索引、与 $or 对比和大小上限。

126 ms
$in 有 10 万个值(去重后 90,619 个)的索引查询耗时
4.4 ms
$in 有 10 万个值时仅规划就用掉的时间(optimizationTimeMicros 4,428 μs)
18,388,903 字节
150 万个整数的 $in 数组大小,超过 16 MB 在客户端被拒绝

耗时随元素数增长

实测 · 本机执行输出
         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 与 $or

实测 · 本机执行输出
  $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)被拒绝。

分步演示

$in 元素数量
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同。50 万个文档;$in 的取值从已存在的 k 里采样;耗时是 pymongo 的 3~5 次中位数
    • 没有验证:$in 里放正则、null、数组元素的行为;$in 与排序、limit 的组合(合并排序);复合索引上 $in 的边界扩展;$in 很大时对计划缓存的影响;分片集群上的 $in 路由(下面分片篇)
    京ICP备2026067502号-1京公网安备11010502063487号