MongoDB:在重负载下的$ unwind结果中的幻像记录

时间:2014-07-09 19:45:39

标签: mongodb aggregation-framework

我有一个简单的元素集合

{_id: n, xs: [...]}    

我正在尝试计算所有数组中的元素总数

db.testRace.aggregate([{ $unwind : "$xs" }, { $group : { _id : null, count : { $sum : 1 } } }])

除非我开始对此系列进行大量更新,否则它的效果很好。在负载较重的更新操作中,我总得错 - 比它应该略大。

可以轻松复制。 首先生成一些测试数据

for(var i = 1; i <= 1000000; i++) {
    db.testRace.insert({_id: i, xs: [i]});
}

然后模拟大量更新

while(true) {
    var id = Math.floor((Math.random() * 1000000) + 1);
    var obj = db.testRace.find({_id: id}).next();
    obj.some="change";
    db.testRace.update({_id: id}, obj);
}

在运行时会进行聚合展开查询。 没有加载我得到了正确的结果 - 1000000.但是当有很多更新时我会得到更大的数字,比如1001456。

如果我像这样运行查询

db.testRace.aggregate([{ $unwind : "$xs" }, {$group: {_id:"$xs", count:{$sum: 1}}}, { $sort : { count : -1 } }, { $limit : 2 }]);

我得到了

"result" : [
    {
        "_id" : 996972,
        "count" : 2
    },
    {
        "_id" : 997789,
        "count" : 2
    }
],

所以似乎聚合计数了两次记录。

是预期的行为还是我聚合错了?

我测试了本地mongodb实例,版本 - 2.4.9

1 个答案:

答案 0 :(得分:1)

由于MongoDB处理读隔离的方式,这是预期的行为。如果您有一个长时间运行的查询(并且读取每个文档的聚合是一个长时间运行的查询),在查询期间对该数据进行更新,则可能会影响查询中是否返回更新的数据 - 具体取决于发生时的情况,你可能会错过一份文件,接收或接收两次。

来自源代码:

  

在收益期间插入,删除或修改任何数据   查询返回的查询可能会也可能不会被该查询返回。该   查询可以返回:没有;以前的数据;之后的数据;或两者   之前的数据和之后的数据。

     

简而言之,查询和查询之间没有隔离   插入/删除/更新。 AKA,READ_UNCOMMITTED。

https://github.com/mongodb/mongo/blob/master/src/mongo/db/exec/plan_stage.h

您的聚合查询正在产生中间查询,在此期间会更新某些数据。这会影响查询结果。