如何使用preload,includes或eager_load在Ruby中优化此方法?

时间:2016-11-11 16:31:47

标签: ruby-on-rails ruby memory memory-management worker

我想减少分配并加快Ruby工作人员的速度。我一直在阅读热切的装载,但我还没有完全理解它。这是方法:

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,它似乎没有改变任何东西:

Screenshot from Skylight showing 4 queries

我是否错误地使用includes,还是应该使用其他内容?

1 个答案:

答案 0 :(得分:1)

您所提供的示例似乎并没有从预先加载中受益匪浅。它的用途是避免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)的一部分是找到不同规模的瓶颈'。我不打算解释所有这些,但这是一个很好的教程:https://samurails.com/interview/big-o-notation-complexity-ruby/

在没有预先加载的示例中,时间复杂度为O(N),这意味着线性'所需时间随着N的值线性增加。但是,如果使用预先加载,则可以在不添加其他查询的情况下增加N,并且它是O(1)复杂度 - 恒定时间。

在您的情况下,您有一个方法可以进行三次查询:

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

确定是否应该使用预先加载的简单方法是检查代码中是否存在循环内发生的任何SQL提取。这不会发生在这里,因此急切的加载不会做太多。例如,如果您正在为 研究列表获取关联数据,那么使用includes会很好。

技术上可能会创建一个获取所有三个表的SQL查询。单个请求中的数据,但我不认为ActiveRecord可以为您做任何事情。但是,这可能是不必要的。如果您不相信,可以尝试writing that SQL yourself并报告效果增益。