【问题标题】:Rails Caching DB Queries and Best PracticesRails 缓存数据库查询和最佳实践
【发布时间】:2010-09-25 13:50:02
【问题描述】:

我网站上的数据库负载变得非常高,所以我该缓存那些每小时被调用 1000 次且结果没有变化的常见查询。 因此,例如在我的城市模型上,我执行以下操作:

def self.fetch(id)   
  Rails.cache.fetch("city_#{id}") { City.find(id) }   
end 

def after_save
  Rails.cache.delete("city_#{self.id}")
end

def after_destroy
  Rails.cache.delete("city_#{self.id}")
end

所以现在当我第一次访问数据库时可以 City.find(1) 但接下来的 1000 次我从内存中得到结果。伟大的。但是大多数对 city 的调用不是 City.find(1) 而是 @user.city.name,其中 Rails 不使用 fetch 而是再次查询 DB ......这是有道理的,但不完全是我想要的。

我可以做 City.find(@user.city_id) 但这很难看。

所以我想问你们。聪明人在做什么?什么是 这样做的正确方法?

【问题讨论】:

    标签: ruby-on-rails caching memcached


    【解决方案1】:

    关于缓存,有几点需要注意:

    值得使用斜线来分隔对象类型和 id,这是 rails 约定。更好的是,ActiveRecord 模型提供了 cacke_key 实例方法,该方法将提供表名和 id、“cities/13”等的唯一标识符。

    对 after_save 过滤器的一个小修正。由于您手头有数据,因此最好将其写回缓存而不是删除它。这样可以节省您一次访问数据库的时间;)

    def after_save Rails.cache.write(cache_key,self) 结尾

    至于问题的根源,如果你不断拉@user.city.name,有两个真正的选择:

    • 将用户的城市名称非规范化到用户行。 @user.city_name(保留 city_id 外键)。此值应在保存时写入。

    -或-

    • 实现您的 User.fetch 方法以预先加载城市。仅当城市行的内容从未更改(即名称等)时才执行此操作,否则您可能会在缓存失效方面打开一罐蠕虫。

    个人意见: 实现基于 id 的基本获取方法(或使用插件)以与 memcached 集成,并将城市名称非规范化为用户行。

    我个人不是缓存模型样式插件的忠实拥护者,我从来没有见过一个可以节省大量开发时间的插件。

    如果您有太多的数据库查询,那么如果您还没有的话,绝对值得检查一下预加载(通过 :include)。这应该是减少数据库查询量的第一步。

    【讨论】:

    • EGUser.where(id: 3).includes(:city).first。对于多对多关系来说,被低估的是join-merge。 EGCity.joins(:restaurants).merge(user.favorite_restaurants),将返回用户喜欢的餐馆多的所有城市,餐馆有许多城市。检查includes and joins。绝对值得学习如何使用强大的查询来减少调用,而不是经历缓存的痛苦。
    【解决方案2】:

    如果您需要加快对随时间变化不大的数据的 sql 查询,则可以使用物化视图。

    matview 将查询结果存储到类似表的结构中 它自己的,可以从中查询数据。无法添加 或删除行,但其余时间它的行为就像 实际表。查询速度更快,matview本身可以 索引。

    在撰写本文时,matviews 在 Oracle 中原生可用 DB、PostgreSQL、Sybase、IBM DB2 和 Microsoft SQL Server。 MySQL 不幸的是,不提供对 matview 的本机支持,但是 是它的开源替代品。

    这里有一些关于如何在 Rails 中使用 matview 的好文章

    sitepoint.com/speed-up-with-materialized-views-on-postgresql-and-rails

    hashrocket.com/materialized-view-strategies-using-postgresql

    【讨论】:

      【解决方案3】:

      我会继续看看Memoization,它现在在 Rails 2.2 中。

      “记忆是一种模式 一次又一次地初始化一个方法 隐藏其价值以供重复使用 使用。”

      最近有一个很棒的Railscast episode,它应该可以让你很好地启动和运行。

      来自 Railscast 的快速代码示例:

      class Product < ActiveRecord::Base
        extend ActiveSupport::Memoizable
      
        belongs_to :category
      
        def filesize(num = 1)
          # some expensive operation
          sleep 2
          12345789 * num
        end
        memoize :filesize
      end
      

      More on Memoization

      【讨论】:

      • 与 Rails.cache 相比,memoization 有什么好处?它如何解决 User.city.name 的问题,Rails 在 City 上进行查找而不是使用缓存数据?
      • Memoization 只会在每个服务器进程的基础上存储结果并且不会使结果无效,因此它相对不适合您的情况并且不打算以这种方式使用。
      【解决方案4】:

      查看cached_model

      【讨论】:

      • memoization 只是包装了 Rails.cache。我认为它不会像您正在寻找的那样帮助模型关联。
      • Memoization 不会包装 Rails 缓存。 Rails.cache 通常是进程之间共享的缓存存储(这样您实际上可以获得缓存优势)。记忆只发生在当前进程中。
      猜你喜欢
      • 2013-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-28
      相关资源
      最近更新 更多