是否可以在Rails中缓存自定义SQL查询?

Lea*_*cim 10 ruby-on-rails

在我的Rails 4应用程序的home_controller中,我执行自定义SQL查询并将结果保存到实例变量

@studentscoring = ActiveRecord::Base.connection.execute sql_string_student
Run Code Online (Sandbox Code Playgroud)

然后,在配置开发中将缓存设置为true config.action_controller.perform_caching = true并重新启动应用程序之后,在视图中围绕相关变量设置缓存.

  <% cache @studentscoring do%>
    <% for lawyer in @studentscoring  %>
    <div class="span2">
      <div class="row">
        <%=  tiny_gravatar_for lawyer['name'], lawyer['email'] %>

      </div>
      ...... #code ommitted
    </div>

    <% end %>
    <% end %>
Run Code Online (Sandbox Code Playgroud)

刷新浏览器三次显示查询分别运行三次,查询的最后一次运行实际上比第一次运行时长.7ms,所以我假设缓存不起作用或者我没有正确执行:) 你能告诉我我做错了什么吗?

不是任何标准的专家,我不明白如何使用<%cache ... do%>语法从视图中触发缓存,因为在加载视图时,控制器查询已经没有了因此,告诉Rails使用缓存副本为时已晚?

从服务器日志...

第一

 (1.1ms)  with cte_scoring as (
 select
 users.id, users.name, users.email,
 (select Coalesce(sum(value),0) from answer_votes where (answer_votes.user_id = users.id) AND (created_at >= Current_Date - interval '7 day')) +
 (select Coalesce(sum(value),0) from best_answers where (best_answers.user_id = users.id) AND (created_at >= Current_Date - interval '7 day')) +
 (select Coalesce(sum(value),0) from contributions where (contributions.user_id = users.id) AND (created_at >= Current_Date - interval '7 day')) total_score
 from
 users
 where
 users.student = 'true') 

select id,
 name,
 email,
 total_score
from cte_scoring
order by total_score desc
limit 5 
Run Code Online (Sandbox Code Playgroud)

第3

  (1.8ms)  with cte_scoring as (
 select
 users.id, users.name, users.email,
 (select Coalesce(sum(value),0) from answer_votes where (answer_votes.user_id = users.id) AND (created_at >= Current_Date - interval '7 day')) +
 (select Coalesce(sum(value),0) from best_answers where (best_answers.user_id = users.id) AND (created_at >= Current_Date - interval '7 day')) +
 (select Coalesce(sum(value),0) from contributions where (contributions.user_id = users.id) AND (created_at >= Current_Date - interval '7 day')) total_score
 from
 users
 where
 users.student = 'true') 

select id,
 name,
 email,
 total_score
from cte_scoring
order by total_score desc
limit 5 
Run Code Online (Sandbox Code Playgroud)

更新

日志显示它正在读取一个片段(在运行上面的查询之后),那么为什么查询会有不同的时间而后面的查询会更慢?如果有一个要读取的片段,我原以为查询根本不会运行.

Read fragment views/id/75/name/Retarded Student/email/retarstudent@gmail.com/total_score/0/id/83/name/Jim Beam/email/jimbean@gmail.com/total_score/0/id/79/name/Weird Student/email/weirdstudent@gmail.com/total_score/0/id/80/name/VegetableSTudent/email/veggiestudent@gmail.com/total_score/0/c9638e467bfd0fbf5b619ab411182256 (0.3ms)
Run Code Online (Sandbox Code Playgroud)

Rya*_*cox 8

从ActiveRecord缓存角度来看:

我理解这一点的方式是ActiveRecord查询缓存是每个Web请求.这意味着如果您在使用缓存的同一请求中运行两次SQL查询,但在每个请求结束时清除缓存.

来源:ActiveRecord :: QueryCache中间件源

(我确实相信`ActiveRecord :: Base.execute调用通常被缓存,就像你使用ActiveRecord查询API进行的查询一样)

如果您只想为应用程序的生命周期(或每几个小时一次)执行一次查询,则可以使用另一个Rails API:缓存API,用于将缓存存储在文件系统,内存,内存缓存或定制商店.缓存/缓存商店的Rails指南.

如果你决定使用Rails.cacheHeroku的开发中心缓存策略有一些代码示例显示你的API Rails.cache的模样.它非常易于使用.

为什么片段缓存不能像您期望的那样工作

视图中的缓存调用意味着您正在定义片段缓存.(请参阅缓存/片段缓存部分Rails指南).这将缓存视图的HTML输出,正如您在日志中看到的那样.

但片段缓存仅适用于HTML.当你执行查询并将结果分配给@studentscoring你在控制器中执行它时(对吗?),然后执行视图.

ActiveRecord查询通常是懒惰的 - 执行被延迟直到真正需要数据,比如迭代记录 - 所以你的技巧可能在使用ActiveRecord查询API时起作用.但是,我猜这些ActiveRecord::Base.execute查询不是懒惰的.我无法证明这一点,但这是你可以进行实验的东西.

因此可以使用您的片段缓存,但您已经为控制器中的查询付出了代价.


Cod*_*lan 7

在查看控制器中缓存查询结果.您可以在一次调用中读取或写回缓存(即,如果数据尚不存在,则将数据设置在缓存中)

def index
  @studentscoring = Rails.cache.fetch("your_cache_key", :expires_in => 5.minutes) do
    ActiveRecord::Base.connection.select_rows(sql_string_student)
  end
end
Run Code Online (Sandbox Code Playgroud)

因此,上面将首先检查缓存"your_cache_key",如果数据存在则将从缓存中返回.如果它不存在,则块将执行,它将在缓存中设置

  • 啊,是的,它看起来像'PG :: Result`无法直接序列化.在这种情况下,使用不同的方法将原始行作为Ruby Arrays获取,然后使用它们.`rows = ActiveRecord :: Base.connection.select_rows(sql_string_student)` (2认同)