使用计数预分配记录

pre-allocation of records using count

本文关键字:预分配 记录      更新时间:2023-09-26

我读到记录的预分配可以提高性能,这应该是有益的,尤其是在处理时间序列数据集的许多记录时。

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
                }
            }
        );
    });
};

我有点担心必须通过承诺可能会减慢速度,并且可能每次都检查计数会抵消预先分配记录的性能优势。

有没有更高性能的方法来处理这个问题?

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

老实说,处理此类操作的最佳方法是首先在分配所有数组元素的情况下执行"upsert",然后仅更新所需的元素。这会减少到"两个"潜在的写入,并且您可以使用批量 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只会在实际发生"更新插入"时执行任何操作。主要情况是与服务器的唯一联系将是单个请求和响应,而不是等待网络通信的"来回"操作。

这通常是"批量"操作的用途。当您还可以向服务器发送一批请求时,它们会减少网络开销。结果显着加快了速度,除了"有序"除外,这两个操作都不是真正依赖于另一个,这是后一种情况下的默认值,并由遗留.initializeOrderedBulkOp()明确设置。

是的,"upsert"中有"一点"开销,但与使用 .count() 进行测试并首先等待该结果相比,开销"更少"。


注意:不确定列表中有 32 个数组条目。你可能的意思是 24,但复制/粘贴让你变得更好。无论如何,正如所证明的那样,有比硬编码更好的方法来做到这一点。