【问题标题】:Is it possible to cache custom sql queries in Rails?是否可以在 Rails 中缓存自定义 sql 查询?
【发布时间】:2013-06-05 08:32:00
【问题描述】:

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

@studentscoring = ActiveRecord::Base.connection.execute sql_string_student

然后,在配置开发config.action_controller.perform_caching = true 中将缓存设置为 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 %>

刷新浏览器 3 次表明查询运行了 3 次,并且最后一次运行查询实际上比第一次运行时间长 0.7 毫秒,所以我假设缓存不起作用或者我没有正确执行:)。你能告诉我我做错了什么吗?

不是任何标准的专家,我不明白如何使用 语法从视图触发缓存,因为在加载视图时还没有控制器查询已经运行,因此告诉 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 

第三

  (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 

更新

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

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)

【问题讨论】:

  • 好问题! ActiveRecord 缓存似乎是非常部落的传说 - 我很难找到回答你问题的明确文档。

标签: ruby-on-rails


【解决方案1】:

在控制器中缓存查询结果。一次调用即可读或写回缓存(即如果缓存中不存在数据,则设置缓存中的数据)

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

所以上面会首先检查缓存中的"your_cache_key",如果数据存在就会从缓存中返回。如果它不存在,则该块将执行并将其设置在缓存中

【讨论】:

  • 这个动作缓存了吗?如果是这样,难道不是在 Rails 4 中不推荐它吗?无论哪种方式,“your_cache_key”是我任意设置的还是生成的?感谢您的帮助。
  • 当我说“在 Rails 4 中不推荐”时,我指的是添加缓存摘要以消除设置缓存版本号的需要。我不确定这是否是您所说的“your_cache_key”
  • 谢谢@FrederickCheung,如果您有时间,能否请您告诉我“your_cache_key”的用途。
  • 这是一个任意值,由您选择。
  • 啊,是的,PG::Result 好像不能直接序列化。在这种情况下,使用不同的方法将原始行获取为 Ruby 数组,然后使用它们。 rows = ActiveRecord::Base.connection.select_rows(sql_string_student)
【解决方案2】:

从 ActiveRecord 缓存的角度来看:

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

来源:ActiveRecord::QueryCache middleware source

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

如果您只想在应用程序的生命周期内执行一次查询(或每隔几个小时执行一次),您可以使用另一个 Rails API:缓存 API 将您的缓存存储在文件系统、内存、memcache 中,或在定制商店中。 The Rails Guide on Caching / Cache Stores

如果您决定使用Rails.cacheHeroku Dev Center on Caching Strategies 有一些代码示例向您展示Rails.cache 的 API 是什么样的。它非常易于使用。

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

您视图中的缓存调用意味着您正在定义片段缓存。 (见Rails Guide on Caching / Fragment caching section)。正如您在日志中看到的那样,这将缓存视图的 HTML 输出。

但是片段缓存只适用于 HTML。当您进行查询并将结果分配给 @studentscoring 时,您是在视图执行之前在控制器中执行的(对吗?)。

ActiveRecord 查询通常是惰性的 - 直到真正需要数据时才会延迟执行,例如迭代记录 - 所以当使用 ActiveRecord 查询 API 时,您的技巧可能会奏效。但是,我猜ActiveRecord::Base.execute 查询并不懒惰。我无法证明这一点,但你可以对它进行实验。

所以你的片段缓存可能会被使用,但你已经为控制器中的查询付出了代价。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 1970-01-01
    • 2019-02-20
    相关资源
    最近更新 更多