2016-03-02 107 views
3

我读过预分配记录可以提高性能,这应该是有益的,特别是在处理时间序列数据集的多个记录时。使用计数预分配记录

updateRefLog = function(_ref,year,month,day){ 
    var id = _ref,"|"+year+"|"+month; 
    db.collection('ref_history').count({"_id":id},function(err,count){ 
     // pre-allocate if needed 
     if(count < 1){ 
      db.collection('ref_history').insert({ 
       "_id":id 
       ,"dates":[{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0},{"count":0}] 
      }); 
     } 

     // update 
     var update={"$inc":inc['dates.'+day+'.count'] = 1;}; 
     db.collection('ref_history').update({"_id":id},update,{upsert: true}, 
      function(err, res){ 
       if(err !== null){ 
        //handle error 
       } 
      } 
     ); 
    }); 
}; 

我有点担心,不必经过一个承诺可能会减缓下来,每次将否定预分配记录的性能优势可能检查计数。

有没有更好的方法来处理这个问题?

+0

你使用的是什么版本的MongoDB ..如果MongoDB 3.0或更新的版本是什么存储引擎?预分配对于MMAP存储引擎来说是一种有用的优化技术,但会增加不支持就地更新的其他存储引擎(例如WiredTiger)的开销。 – Stennie

回答

1

“预先分配”的一般说明是关于导致文档“增长”的“更新”操作的潜在成本。如果这导致文档大小大于当前分配的空间,则文档将被“移动”到磁盘上的另一位置以容纳新空间。这可能是昂贵的,因此一般建议将文档编写成最终的“大小”。

诚实地说,处理这种操作的最好方法是在分配所有数组元素的时候先做一个“upsert”,然后只更新requried元素的位置。这将减少对“两节”的潜力写,你可以使用批量API方法进一步减少到一个单一的“过线”操作:

var id = _ref,"|"+year+"|"+month; 
var bulk = db.collection('ref_history').initializeOrderedBulkOp(); 

bulk.find({ "_id": id }).upsert().updateOne({ 
    "$setOnInsert": { 
     "dates": Array.apply(null,Array(32)).map(function(el) { return { "count": 0 }}) 
    } 
}); 

var update={"$inc":inc['dates.'+day+'.count'] = 1;}; 
bulk.find({ "_id": id }).updateOne(update); 

bulk.execute(function(err,results) { 
    // results would show what was modified or not 
}); 

或者因为更新的驱动程序有利于一致性彼此,“批量“部分已经退居WriteOperations代替普通阵列:

var update={"$inc":inc['dates.'+day+'.count'] = 1;}; 

db.collection('ref_history').bulkWrite([ 
    { "updateOne": { 
     "filter": { "_id": id }, 
     "update": { 
      "$setOnInsert": { 
       "dates": Array.apply(null,Array(32)).map(function(el) { 
        return { "count": 0 } 
       }) 
      } 
     }, 
     "upsert": true 
    }}, 
    { "updateOne": { 
     "filter": { "_id": id }, 
     "update": update 
    }} 
],function(err,result) { 
    // same thing as above really 
}); 

在这两种情况下,$setOnInsert作为唯一块如果只会做任何事情‘更新插入’实际发生。主要情况是与服务器的唯一联系将是单个请求和响应,而不是等待网络通信的“来回”操作。

这通常是“批量”操作的用途。当你可能向服务器发送一批请求时,它们会减少网络开销。结果显着地加快了速度,并且除了“ordered”的例外(这是后一种情况下的默认设置)以及由传统.initializeOrderedBulkOp()明确设置的例外之外,这两种操作都不依赖于其他操作。

是的,在“upsert”中有一个“小”的开销,但是比用.count()进行测试并且首先等待这个结果要少一些。


N.B不确定您的列表中的32个阵列条目。你可能意味着24,但复制/粘贴得到了你的更好。正如所证明的,无论如何,有比硬编码更好的方法。

+0

我的意思是它是31个条目(最大月份为天),但我将它切换为日期而不是数组的键,这样我可以从1开始,而不是从0开始。 – Daniel

+0

@Daniel其中也是有效的,但当然,除非您再次“预先分配”空间,否则文档有可能再次移动。无论你的实际最终实现是什么,那么做最好的事情就是“上位”,而不是等待计数返回。那是你的问题,所以答案就是这样。即使你决定只尝试一次完整的写入**,并且即使你之后删除了所有的键/数组条目,你仍然需要决定以某种方式写入新的文档。 upserts王牌'.find()'然后'.insert()'每次。 –

+0

我发现,在我的测试中,最快的是不要预先分配,查找方法似乎超过批量插入一个很小的余量。这很可能是由于我的设置引起了这种情况,但我暂时将推迟性能调整。 – Daniel