2017-04-25 27 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高度优化的引擎。