必须显式指定$merge主节点写入策略,用$sample+$addfields生成可控随机数据,$unwind需防爆炸并立即$replaceroot,生成后必用$count和$group验证分布。

$merge 阶段必须显式指定主节点写入策略
在副本集上运行聚合生成测试数据,不能只靠 $out 或默认 $merge。MongoDB 6.0 要求:若想让聚合管道在从节点执行(节省主节点压力),必须同时满足三个条件——集群所有节点的 featureCompatibilityVersion ≥ 6.0、读取偏好设为 secondaryPreferred 或更激进、且驱动版本支持从节点执行 $merge。但绝大多数生产环境驱动(如 Node.js 5.x、Python PyMongo 4.7 以下)仍会把 $merge 自动路由到主节点。所以别依赖“自动分发”,直接用 readPreference: primary 显式声明,避免因驱动行为不一致导致写入失败或数据延迟。
用 $sample + $addFields 构造可控规模的随机文档
别用循环插入模拟测试数据:$sample 是最轻量的起点,配合 $addFields 注入时间戳、ID 偏移、状态枚举等字段,就能批量生成结构一致的样本。关键点:
-
$sample必须作用于一个已有集合(哪怕只有 1 条种子文档),否则报错"$sample requires a non-empty collection" - 用
$addFields生成伪随机值时,避免$rand单独使用——它每次计算结果不可预测,会导致$group或去重逻辑异常;改用$mod+$sum组合构造确定性序列,例如{ rollno: { $add: [ 1000, { $mod: [ "$_id", 500 ] } ] } } - 若需日期字段,用
$dateAdd(6.0 新增)比$add更安全,能正确处理月份溢出,例如{ testDate: { $dateAdd: { startDate: "$baseDate", unit: "day", amount: { $mod: [ "$_id", 30 ] } } } }
嵌套数组字段要防爆炸,$unwind 后立即 $replaceRoot
测试数据常需模拟订单明细、考勤记录等嵌套结构。直接 $unwind 数组可能让单文档膨胀成上百条,拖慢整个管道。正确做法:
- 先用
$map+$range生成固定长度数组(如 3–5 条明细),再$unwind,避免无界增长 -
$unwind必须加preserveNullAndEmptyArrays: true,否则遇到空数组字段整个管道中断 - 展开后立刻用
$replaceRoot把数组元素提为根文档,而不是留着atndnc: { attend: true, date: ... }这种嵌套结构——后续$group或索引都难优化 - 如果只是要“看起来像”嵌套数据,用
$literal构造静态 JSON 片段更省资源,例如{ items: { $literal: [ { sku: "A001", qty: 2 }, { sku: "B002", qty: 1 } ] } }
生成后验证数据分布,别跳过 $count 和 $group 检查
聚合生成测试数据最容易忽略验证环节。看似跑完就结束,但字段缺失、类型错乱、重复 ID 等问题往往要到下游应用才暴露。每次生成后必须加两步检查:
- 用
$count验证输出文档总数是否符合预期,例如{ $count: "total" },防止$match条件意外过滤掉全部数据 - 对关键字段(如
status、category)做快速分布统计:{ $group: { _id: "$status", count: { $sum: 1 } } },确认枚举值比例合理 - 如果用了
$merge写入现有集合,务必检查whenMatched行为——默认是replace,可能误删历史数据;测试场景建议显式设为keepExisting
真正卡住人的不是语法,而是生成后的数据“看起来对、用起来错”:比如时间字段是字符串不是 Date 类型,或 _id 重复导致后续插入失败。每次生成完,花 10 秒执行一条 db.testdata.findOne() 看实际结构,比调半天应用日志快得多。











