Предварительное распределение записей с использованием счетчика

Я читал, что предварительное выделение записи может улучшить производительность, что должно быть особенно полезно при обработке многих записей набора данных временного ряда.

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

Я немного обеспокоен тем, что выполнение обещания может замедлить это, и, возможно, проверка на счетчик каждый раз сводит на нет преимущество в производительности при предварительном выделении записи.

Есть ли более эффективный способ справиться с этим?

1 ответ

Решение

Общее утверждение о "предварительном выделении" относится к потенциальной стоимости операции "обновления", которая приводит к "росту" документа. Если это приводит к тому, что размер документа больше, чем выделенное в настоящее время пространство, документ будет "перемещен" в другое место на диске для размещения нового пространства. Это может быть дорогостоящим, и, следовательно, общая рекомендация изначально написать документ, соответствующий его возможному "размеру".

Честно говоря, лучший способ справиться с такой операцией состоит в том, чтобы сначала выполнить "upsert" со всеми выделенными элементами массива, а затем только обновить требуемый элемент в позиции. Это уменьшило бы до двух возможных записей, и вы можете дополнительно сократить до одной операции "по проводам", используя методы Bulk 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() и ждем этого результата первым.


NB Не уверен насчет 32 записей массива в вашем списке. Возможно, вы имели в виду 24, но копирование / вставка взяли верх над вами. В любом случае, как демонстрируется, есть более эффективные способы, чем жесткое кодирование.

Другие вопросы по тегам