Mongoid每个+ set vs Critera #set vs update_all + $ addToSet

时间:2017-04-25 11:50:15

标签: ruby mongodb mongoid

我想知道什么是更好的性能/内存:迭代集合中的所有对象并调用set / add_to_set或直接在Criteria上调用set / add_to_set或使用set / add_to_set使用update all。

# 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 个答案:

答案 0 :(得分: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.每个选择器

我找到如下所示的命令,然后是多个getMore-s:

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

我也获得了大量的update-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]回复所有匹配的子集。剩下的时间用于下一次迭代。
  • [loop] MongoID以哈希格式获取匹配项的数组。 MongoID解析每个条目并为其初始化User类。那是一项昂贵的操作!
  • [loop]对于上一步中的每个User实例,MongoID会生成更新命令并将其发送给serve。插座也很贵。
  • [loop] MongoDB获取命令并完成收集直到第一次匹配。更新比赛。它很快,但在循环中累加了一次。
  • [loop] MongoID解析响应并相应地更新其User实例。昂贵且不必要。

选项3.在选择器

上应用add_to_set

在引擎盖下它相当于选项1.它的CPU和内存开销对于这个问题来说并不重要。

结论

选项2速度慢得多,基准测试没有意义。在我尝试的特定情况下,它导致了对MongoDB的1000次请求和1000次User类初始化。选项1和3导致对MongoDB的单个请求,并依赖于MongoDB高度优化的引擎。