我的Rails应用程序,查询占用时间太长。它使用postgresql DB,查询由一个表组成,包含数千条记录。
statistics_controller.rb
all_data = Usagedata.select([:start_time, :end_time, :node_count, :processors, :id, :wall_duration, :local_user_id])
.where(Usagedata.arel_table[(:wall_duration)].not_eq("0"))
.in_range( @from_date, @to_date)
if @user
all_data = all_data.by_user(@user)
end
all_data = all_data.to_a #Forcing to make query
@data = all_data = all_data.to_a
我想要做的是在Rails服务器端应用程序缓存中保留主查询结果(没有in_range和用户语句)并每小时更新一次数据。
应缓存的部分代码:
Usagedata.select([:start_time, :end_time, :node_count, :processors, :id, :wall_duration, :local_user_id])
.where(Usagedata.arel_table[(:wall_duration)].not_eq("0"))
缓存记录的使用
除了该客户端,还可以从日历@from_date和@to_date中选择日期范围。日期之间的时间段可以是1天〜3年。 (这就是为什么缓存应该存储来自数据库表的所有记录。)数据用于绘制图表并显示/计算最高用户统计信息。
我尝试过@MrTheWalrus解决方案
@statistics = Rails.cache.fetch('usagedata', :expires_in => 24.hours) do
Usagedata.select([:start_time, :end_time, :node_count, :processors, :id, :wall_duration, :local_user_id])
.where(Usagedata.arel_table[(:wall_duration)].not_eq("0")).all
end
但是这样我就不能让我的子查询工作了:
all_data = @statistics.in_range( @from_date, @to_date)
if @user
all_data = all_data.by_user(@user)
end
这给了我一个错误:
undefined method `in_range' for #<Array:0x007fa5ecc77588>
虽然我在Usagedata模型中定义了in_range,如下所示:
def self.in_range(from_date, to_date)
where("start_time <= :to AND end_time >= :from", :from => from_date, :to => to_date)
end
我做错了什么?
编辑:感谢@Craig Ringer解决方案,我已成功解决了此处所述的索引问题:
整个应用程序似乎非常慢。我究竟做错了什么?可能我需要添加索引但是如何添加?
Usagedata Load (243.4ms) SELECT start_time, end_time, node_count, processors, id, wall_duration, local_user_id FROM "usagedata" WHERE ("usagedata"."wall_duration" != 0) AND (start_time <= '2013-09-02 20:59:59.999999' AND end_time >= '2013-05-05 21:00:00.000000')EXPLAIN (1.9ms) EXPLAIN SELECT start_time, end_time, node_count, processors, id, wall_duration, local_user_id FROM "usagedata" WHERE ("usagedata"."wall_duration" != 0) AND (start_time <= '2013-09-02 20:59:59.999999' AND end_time >= '2013-05-05 21:00:00.000000')
EXPLAIN for: SELECT start_time, end_time, node_count, processors, id, wall_duration, local_user_id FROM "usagedata" WHERE ("usagedata"."wall_duration" != 0) AND (start_time <= '2013-09-02 20:59:59.999999' AND end_time >= '2013-05-05 21:00:00.000000')
QUERY PLAN
---------------------------------------------------------------------------------------
Seq Scan on usagedata (cost=0.00..4558.02 rows=7989 width=34)
Filter: ((wall_duration <> 0) AND (start_time <= '2013-09-02 20:59:59.999999'::timestamp without time zone) AND (end_time >= '2013-05-05 21:00:00'::timestamp without time zone))
(2 rows)
答案 0 :(得分:2)
Craig Ringer的评论已经讨论过索引,所以我只想谈谈缓存。
您包含的缓存代码的问题是您缓存的内容是ActiveRecord::Relation
- 基本上只是等待运行的SQL查询,而不是该查询的结果。缓存关系意味着每次从缓存加载时,它仍然必须执行查询,这是需要很长时间的部分。在最后添加.all
以强制查询实际运行 - 这将确保缓存结果,而不是查询:
@statistics = Rails.cache.fetch('usagedata', :expires_in => 24.hours) do
Usagedata.select([:start_time, :end_time, :node_count, :processors, :id, :wall_duration, :local_user_id]).
where(Usagedata.arel_table[(:wall_duration)].not_eq("0")).all
end
修改强>
您无法就此.in_range
致电的原因是.in_range
修改了查询(通过添加WHERE
子句)。一旦运行了查询并缓存了结果,就无法以这种方式对其进行修改 - 缓存查询结果的重点在于您运行查询一次并多次使用结果 - 如果查询更改,则不是一个选项。
假设添加索引尚未解决您的问题,我的建议是您在Ruby中而不是在数据库中过滤结果。假设您已经填充了缓存(通过Whenever或以其他方式):
from_time = 1.week.ago
to_time = 1.day.ago
@statistics = Rails.cache.fetch('usagedata')
@filtered_statistics = @statistics.select do |item|
item.start_time < to_time && item.end_time > from_time
end
答案 1 :(得分:1)
带有start_time
索引过滤器子句的(end_time
,WHERE ("usagedata"."wall_duration" != 0)
)的部分索引将使此查询更快。甚至是(start_time, end_time)
上的非部分索引。
这可能使客户端缓存变得不必要。如果没有,请查看Rails是否支持创建和管理服务器端物化视图。