1

我在 MongoDB 上有一个非常简单的 Map/Reduce 函数,它旨在从集合中返回一组数据的平均值。一切似乎都很好,除了答案是错误的,在一种情况下是 2 倍。

这是我的 Map/Reduce 函数——我不得不混淆“diff”值的来源,但从日志中返回的打印语句中我已经验证它是正确的:

    var mapFunction = function() {
    if (this.fieldId==1234) {
        print(diff);    
    }
    emit(this.fieldId,diff);
};

var reduceFunction = function(keyId, viewTime) {
    var count = viewTime.length;
    var total = 0;
    for (idx = 0; idx < viewTime.length; idx++) {
        total+=viewTime[idx];
    }
    if (keyVidId==1234) {
        print('1234: ' + total/count);  
    }
    return total/count;
};

运行此之后,对于特定记录 1234,我得到的结果大约是我从 MySQL 迁移之前得到的结果的两倍也是我在决定使用 Map/Reduce 以实现可伸缩性等之前使用的聚合框架得到的结果的两倍其他记录也有错误,但一般不会差那么多。

最初 reduceFunction 使用 Array.avg 但我转换为手动平均值来尝试调试。

有问题的数据大约是 23,000 个文档,每个 diff 往往是一个非常大的整数。

我浏览了日志,试图找出问题所在,实际上使用 LibreOffice Calc 手动平均了日志中吐出的 diff 值并得到了正确的结果,因此错误出现在 reduce 函数实现中。

我注意到在日志中有多行显示“1234:”,就好像单个keyId多次调用reduce函数一样-我不太清楚它在下面是如何工作的,但我想它会拆分将工作负载转换为多个函数调用,然后在最后组合,这意味着它必须对结果进行加权以获得正确的平均值……我想这可能是问题所在,但我不确定。我也担心它是一个 int32 溢出(因为所有差异的总和大于最大值),但在 python 中对有问题的数字进行一些修改似乎并非如此。

希望有人可以阐明MongoDB在后台做什么以及我做错了什么......

谢谢!

4

1 回答 1

0

你做错了什么是计算每个减少的东西(基于总数和总和的平均值),应该在 finalize 函数中完成(这是可选的,但如果提供的话,每个键值只会运行一次)。

因为reduce函数可能被调用0次、1次或多次,所以你永远不能假设你会在reduce中得到一个键的所有发射值的数组。

这意味着您应该为每个键发出一个对象 {total:1, value:diff} ,然后在 reduce 中只增加这些对象以累积每个键的所有值。

在 finalize 函数中,您可以进行除法以获得适当的平均值。

这个例子就是这样做的。

于 2013-05-15T22:50:39.237 回答