2016-03-02 102 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