在Rails中处理大量查询

时间:2017-07-12 23:58:50

标签: ruby-on-rails ruby postgresql sidekiq

使用Rails和Postgres处理大型结果集的最佳方法是什么?直到今天我都没有遇到问题,但现在我正在尝试返回@network_hosts = [] @host_count = 0 @company.locations.each do |l| if l.grace_enabled == nil || l.grace_enabled == false l.network_hosts.each do |h| @host_count += 1 @network_hosts.push(h) @network_hosts.sort! { |x,y| x.ip_address <=> y.ip_address } @network_hosts = @network_hosts.first(5) end end end 的124,000记录对象,它有效地帮助了我的开发服务器。

我的主动记录并不是最漂亮的,但我很确定清理它并不会对性能有所帮助。

@network_hosts

最后,我需要能够将@network_hosts返回到控制器以便处理到视图中。

这是Sidekiq能够提供帮助的东西,还是会有那么长的时间?如果Sidekiq是要采取的路径,那么当作业以异步方式运行时,如何处理页面加载时没有{{1}}对象?

2 个答案:

答案 0 :(得分:1)

我相信你想(1)摆脱所有循环(你已经进行了大量的查询)和(2)用你的AR查询而不是在数组中进行排序。

也许是这样的:

NetworkHost.
  where(location: Location.where.not(grace_enabed: true).where(company: @company)).
  order(ip_address: :asc).
  tap do |network_hosts|
    @network_hosts = network_hosts.limit(5)
    @host_count = network_hosts.count
  end

这样的事情应该在单个数据库查询中完成。

我必须对你的关联如何设置以及你正在寻找grace_enabled不为真(ni或false)的位置做出一些假设。

我没有测试过这个,所以它可能是错误的。但是,我认为方向是正确的。

答案 1 :(得分:1)

要记住的事情,Rails不会执行任何SQL查询,直到实际需要查询结果。 (我将使用User而不是NetworkHost,因此我可以随时向您显示控制台输出)

@users = User.where(first_name: 'Random');nil # No query run
=> nil
@users # query is now run because the results are needed (they are being output to the IRB window)
#  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."first_name" = $1 LIMIT $2  [["first_name", "Random"], ["LIMIT", 11]]
# => #<ActiveRecord::Relation [...]>
@users = User.where(first_name: 'Random') # query will be run because the results are needed for the output into the IRB window   
#  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."first_name" = $1 LIMIT $2  [["first_name", "Random"], ["LIMIT", 11]]
# => #<ActiveRecord::Relation [...]>

为什么这很重要?它允许您存储要在实例变量中运行的查询,而不是执行它,直到您到达可以使用ActiveRecord::Batches的一些好方法的视图。特别是,如果您在迭代@network_hosts时有一些视图(或导出函数等),则可以使用find_each

# Controller
@users = User.where(first_name: 'Random') # No query run

# view
@users.find_each(batch_size: 1) do |user|
  puts "User's ID is #{user.id}"         
end
#  User Load (0.5ms)  SELECT  "users".* FROM "users" WHERE "users"."first_name" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["first_name", "Random"], ["LIMIT", 1]]
#  User's ID is 1
#  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."first_name" = $1 AND ("users"."id" > 1) ORDER BY "users"."id" ASC LIMIT $2  [["first_name", "Random"], ["LIMIT", 1]]
#  User's ID is 2
#  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."first_name" = $1 AND ("users"."id" > 2) ORDER BY "users"."id" ASC LIMIT $2  [["first_name", "Random"], ["LIMIT", 1]]
# => nil

您的查询在视图之前不会执行,现在它一次只能将1,000条记录(可配置)加载到内存中。一旦它到达那1,000条记录的末尾,它将自动运行另一个查询以获取接下来的1,000条记录。所以你的内存更加理智,代价是额外的数据库查询(通常非常快)