给出一个“rereduce=true”分支的代码示例并解释其输入输出差异。

解读

国内一线互联网公司(阿里、腾讯、字节、美团等)在招聘 CouchDB 内核/运维/云原生方向 时,常把 reduce/rereduce 语义 作为“手写视图”环节的必考题。
面试官真正想看的是:

  1. 你是否理解 “rereduce=true” 时 CouchDB 传进来的值已经是上一轮 reduce 的聚合结果
  2. 能否写出 幂等且可累加 的代码;
  3. 是否知道 输入结构差异(第一次 reduce 收到的是 emit 的原始 value,rereduce 收到的是上一轮 reduce 的输出)。
    答不出“输入差异”会直接被判定为“只写过 Map 没写过 Reduce”,面试基本结束。

知识点

  1. reduce 阶段两次调用
    • 第一次:rereduce=false,values[] 里是 map 函数 emit 的原始值;
    • 第二次及以后:rereduce=true,values[] 里是上一轮 reduce 返回的聚合对象。
  2. 输入长度:rereduce=false 时 values 长度等于当前分片内相同 key 的 emit 次数;rereduce=true 时 values 长度等于需要合并的上游分片数(默认 1~8)。
  3. 输出约束:必须返回 与 rereduce=false 时相同结构 的 JSON,否则后续再 rereduce 会类型错乱。
  4. 国内实战:移动端日志上报、离线订单同步等场景,都用 rereduce 做增量聚合 来降低网络传输。

答案

以下示例计算每个品类的 销售总额 & 订单笔数,兼容两级 rereduce,可直接粘进 CouchDB 2.3/3.x 的 design doc:

// map 函数
function (doc) {
  if (doc.type === 'order' && doc.amount != null) {
    emit(doc.category, {sum: doc.amount, count: 1});
  }
}

// reduce 函数
function (keys, values, rereduce) {
  var acc = {sum: 0, count: 0};
  for (var i = 0; i < values.length; i++) {
    acc.sum   += values[i].sum;
    acc.count += values[i].count;
  }
  return acc;
}

输入输出差异验证(用 curl 本地 5984 端口,国内开发机常用):

  1. rereduce=false(CouchDB 第一次 reduce)
    请求:

    POST /orders/_design/sales/_view/byCategory?group_level=1
    

    输入 values 示例:

    [{sum:12.5,count:1}, {sum:30,count:1}, {sum:7.5,count:1}]
    

    输出:

    {"rows":[
      {"key":"手机配件","value":{"sum":50.0,"count":3}}
    ]}
    
  2. rereduce=true(CouchDB 把多个分片结果再次合并)
    输入 values 变成上一轮 reduce 的输出:

    [{sum:50,count:3}, {sum:200,count:10}]
    

    输出:

    {"sum":250,"count":13}
    

关键差异一句话总结
rereduce=false 时 values 是“原始订单粒度的值”;rereduce=true 时 values 是“已经聚合过的中间结果”,但代码结构必须完全一致,否则 CouchDB 会抛 reduce_overflow_error

拓展思考

  1. 国内大数据量优化:当单个品类日均千万条时,reduce 输出过大会触发 reduce_overflow_error,此时应:
    • 在 reduce 里 只保留预估值(HyperLogLog、分位数摘要),或
    • group_level 调小,让 rereduce 层数变多,单层输入变少。
  2. 与 Cloudant 兼容:IBM Cloudant 国内节点(香港、上海)对 rereduce 有 512 KiB 返回值限制,务必在代码里做 JSON.stringify(result).length < 500*1024 的防御。
  3. 面试加分点:主动提到 “二次 reduce 幂等性” 和 “分片迁移后触发 rereduce” 的坑,能让面试官直接给出“基础扎实,可进下一轮”的评价。