如何使用预加载、包含或 eager_load 在 Ruby 中优化此方法?

How can I optimise this method in Ruby using preload, includes, or eager_load?

我想减少分配并加快 Ruby worker 的速度。我一直在阅读有关预加载的信息,但我还没有完全理解它。方法如下:

def perform(study_id, timestamp)
  study = Study.includes(:questions, :participants).find(study_id)
  questions = study.questions.not_random.not_paused
  participants = study.participants
  return unless questions && participants

  end_timestamp = timestamp_window(timestamp)

  participants.each do |participant|
    process_participant(participant, questions, timestamp, end_timestamp, study)
  end
end

我希望 Study.includes() 会减少数据库查询的数量,但是看看 Skylight,它似乎没有任何改变:

我是在使用 includes 不正确,还是应该使用其他东西?

您给出的示例似乎并没有从预加载中获益太多。它的用途是避免 N+1 查询;像这样:

User.first(100).each do |user|
  comments = user.comments
end

这将对 100 个用户进行 1 次查询,对评论进行 100 次查询。这就是为什么它被称为 N+1(这里 N 是 100)。

为防止这种情况发生,您可以使用预先加载:

User.first(100).includes(:comments).each do |user|
  comments = user.comments
end

现在它进行两个查询 - 一个针对用户,一个针对评论。它进行 2 次查询而不是 1 次查询的事实不是问题。优化(大 O)的一部分是在不同 'scales' 处找到瓶颈。我不打算解释所有这些,但这是一个很好的教程:https://samurails.com/interview/big-o-notation-complexity-ruby/

在没有预加载的例子中,时间复杂度为O(N),即'linear'。所需时间随 N 的值线性增加。但是,如果您使用预先加载,则可以增加 N 而无需添加额外的查询,并且它的复杂度为 O(1) - 常数时间。

在您的例子中,您有一个进行三个查询的方法:

  • 学习(找一个)
  • 相关问题
  • 相关参与者

确定是否应该使用预先加载的一种简单方法是检查代码中是否有任何 SQL 循环内发生的抓取。这不会在这里发生,所以急切加载不会做太多。例如,如果您要为 列表的 研究获取关联数据,则最好使用 includes

从技术上讲,可能可以进行 SQL 查询以在单个请求中获取所有三个表的数据,但我认为 ActiveRecord 无法为您做任何事情。不过,这可能是不必要的。如果您不确定,可以尝试 writing that SQL yourself 并报告性能提升。