Rails缓存数据库查询和最佳实践

我网站上的数据库负载变得非常高,所以现在是时候缓存常见查询,这些查询每小时调用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),但那很难看。

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

关于缓存,有几个小问题:

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

对after_savefilter进行一次小修正。 由于您手头有数据,因此您可以将其写回缓存而不是删除它。 这样可以省去一次数据库;)

 def after_save
   Rails.cache.write(cache_key,个体经营)
结束

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

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

-要么-

  • 实施您的User.fetch方法以急切加载城市。 只有在城市行的内容永远不会改变(即名称等)时才这样做,否则你可能会在缓存失效方面打开一堆蠕虫。

个人观点:实现基于id的基本fetch方法(或使用插件)与memcached集成,并将城市名称反规范化为用户的行。

我个人并不是缓存模型风格插件的忠实粉丝,我从来没有见过一个节省了大量开发时间的插件,我还没有匆忙成长。

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

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

“记忆是一种初始化方法的模式,然后将其价值存储起来以便重复使用。”

最近有一个很棒的Railscast剧集应该可以帮助你顺利运行。

来自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 

更多关于记忆

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

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

在撰写本文时,matviews本身可在Oracle DB,PostgreSQL,Sybase,IBM DB2和Microsoft SQL Server中使用。 不幸的是,MySQL并没有为matviews提供原生支持,但是有一些开源替代品。

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

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

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

看看cached_model