2017-04-25 36 views
0

我想知道什麼是更好的性能/內存明智:迭代集合中的所有對象並調用set/add_to_set或直接在Criteria上調用set/add_to_set或使用用set/add_to_set更新全部。

# update_all 
User.where(some_query).update_all(
    { 
    '$addToSet': { 
     :'some.field.value' => :value_to_add 
    } 
    } 
) 

# each do + add_to_set 
User.where(some_query).each do |user| 
    user.add_to_set(:'some.field.value' => :value_to_add) 
end 

# Criteria#add_to_set 
User.where(some_query).add_to_set(
    :'some.field.value' => :value_to_add 
) 

任何輸入表示讚賞。謝謝!

回答

1

我啓動了帶有詳細標誌的MongoDB服務器。這就是我得到的。

選項1. update_all塗布在選擇

2017-04-25 COMMAND command production_v3.$cmd command: update { update: "products", updates: [ { q: { ... }, u: { $addToSet: { test_field: "value_to_add" } }, multi: true, upsert: false } ], ordered: true } 

我去掉了一些輸出,使得更容易閱讀。流程如下:

  • MongoID生成一個指定查詢和更新的單個命令。
  • MongoDB服務器獲取命令。它通過收集並一次性更新每個比賽[模糊]。

注意!您可以從源代碼中學習,或視爲理所當然。由於根據我的術語,MongoID在步驟1中生成要發送的命令,因此它不檢查模型。例如如果'some.field.value'不是模型User中的字段之一,那麼該命令仍然會通過並保留在DB上。

選項2.旺火上選擇

我得到find命令像下面接着多個getMore-S:

2017-04-25 COMMAND command production_v3.products command: find { find: "products", filter: { ... } } 0ms 

我也得到更新-S一個龐大的數字:

2017-04-25 COMMAND command production_v3.$cmd command: update { update: "products", updates: [ { q: { _id: ObjectId('52a6db196c3f4f422500f255') }, u: { $addToSet: { test_field: { $each: [ "value_to_add" ] } } }, multi: false, upsert: false } ], ordered: true } 0ms 

流程與第一個選項完全不同:

  • MongoID發送一個簡單的查詢到MongoDB服務器。如果你的集合足夠大,並且查詢涵蓋了它的一個重要組成部分,則下面的情況會發生在循環中:
  • [loop]響應所有匹配的子集。留下其餘的下一個迭代。
  • [循環] MongoID獲取哈希格式的匹配項數組。 MongoID解析每個條目併爲其初始化User類。這是一個昂貴的操作!
  • [loop]對於上一步驟中的每個用戶實例,MongoID生成更新命令並將其發送到服務器。套接字也很貴。
  • [循環] MongoDB獲取命令並遍歷集合,直到第一次匹配。更新匹配。它很快,但在循環中累加一次。
  • [循環] MongoID解析響應並相應地更新其用戶實例。昂貴和不必要的。

選項3 add_to_set塗布在選擇

引擎蓋下它相當於選項1它的CPU和內存開銷是不重要的問題的緣故。

結論:

選項2慢得多,以至於在基準測試中沒有意義。在我嘗試的特定情況下,它導致對MongoDB的1000個請求和1000個用戶類初始化。選項1和3導致對MongoDB的一次請求,並依賴於MongoDB高度優化的引擎。