使用Rails优化DB的导出

时间:2014-04-29 12:39:18

标签: ruby-on-rails ruby postgresql

我有一个RoR应用程序,其中包含一个用于管理应用程序的API,每个应用程序都包含配方(以及组,成分,测量值)。

用户完成配方管理后,会下载整个应用程序的JSON文件。因为每个应用程序可能有数百个配方,所以文件可能很大。这也意味着需要进行大量的数据库调用才能获得所有需要的数据。

现在因此,下载应用程序的请求可能需要30秒,有时甚至更多。

我当前的代码看起来像这样:

application.categories.each do |c|
  c.recipes.each do |r|
      r.groups.each do |r|
          r.ingredients.each do |r|

在每个循环中,我将数据存储在HASH中,然后将其提供给用户。

我的问题是:我从哪里开始?

  • 有没有办法在一次查询中从数据库中获取所有数据?通过查看日志,我可以看到它运行了数百个查询。
  • 如果上述解决方案仍然很慢,我应该将这些内容放入后台进程,然后通过电子邮件向用户发送链接(或类似内容)?

2 个答案:

答案 0 :(得分:1)

当然有一些方法可以同时获取更多数据。这是通过Rails includesjoins完成的,具体取决于您的需求。有关详细信息,请参阅this article

基本思想是您可以在表之间加入,以便每次都不生成新查询。执行application.categories时,这是一个查询。对于每个类别,您将执行另一个查询:c.recipes - 这会创建N + 1个查询,其中N是您拥有的类别数。相反,您可以将它们包括在内,以创建1或2个查询(取决于Rails的功能)。

基本语法很简单:

Application.includes(:categories => :recipes).each do |application| ...

这会生成1(或2 - 再次,请参阅文章)查询,该查询一次性抓取所有应用程序,其类别和每个类别的收件人。你也可以对这些团体和成分进行研究。

至于把工作放在后台,我的建议是只有一个加载图像,或者通过使用进度条来获得幻想。

答案 1 :(得分:1)

首先,我必须假设存在所需的has_manybelongs_to关联。

通常你可以做类似

的事情
c.recipes.includes(:groups)

甚至

c.recipes.includes(:groups => :ingredients)

将立即获取食谱和组(和成分)。

但是,由于你有一个非常大的数据集IMO,如果你将这种技术限制在最深层次会更好。

最有用的方法是同时使用find_eachincludes

find_each批量提取项目以保持内存使用率低)

或许类似

application.categories.each do |c|
  c.recipes.find_each do |r|
    r.groups.includes(:ingredients).each do |r|
      r.ingredients.each do |r|
        ...
      end
    end
  end
end

现在即使这可能需要相当长的时间(对于http请求),因此您可以考虑使用一些异步处理,其中客户端将生成将由服务器作为后台作业处理的请求,并且当准备就绪,您可以提供下载链接(或发送电子邮件)给客户。

Resque是处理异步部分的一种可能解决方案。