Rails的不同缓存策略

Posted AI启示录

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rails的不同缓存策略相关的知识,希望对你有一定的参考价值。

动作缓存

至此,您已经了解了页面缓存的主要的优势及主要的缺点:对于多数页面检索而言,根本无需考虑使用 Rails。页面缓存的优势是速度快。缺点是缺少灵活性。如果想要基于应用程序内的条件 — 例如,身份认证 — 来缓存整个页面,那么可以使用动作缓存。

动作缓存与页面缓存的工作方式大体相同,但在流程上稍有差别。Rails 在呈现动作前会实际调用控制器。如果由该动作呈现的页面已经存在于缓存内,那么 Rails 就会在缓存内呈现页面而不是重新加以呈现。由于现在使用了 Rails,因此动作缓存的速度要比页面缓存慢一些,但其优点还是很明显的。几乎所有的 Rails 认证模式都会在控制器上使用 before 过滤器。动作缓存让您能够利用认证及控制器上的任何过滤器。

从语句构成的角度来看,动作缓存与页面缓存也应该十分类似,只有指令不太一样。清单 6 显示了如何使用 caches_action 指令。

清单 6. 启用动作缓存

1

2

3

class AboutController < ApplicationController

  caches_action :secret_page, :secret_list

end


缓存到期失效以及清理器的工作方式也应该相同。我们不使用动作缓存的原因与我们不使用页面缓存的原因是一样的,但分段缓存对我们来说更重要一些。

缓存页面分段

借助部分缓存,可以缓存页面的一部分,所缓存的内容很多时候都是布局之类的。要使用分段缓存,开发人员需要先确定分段,方法是通过在 Web 页面上直接放上 rhtml 指令来包围一块内容,如清单 7 所示。在 ChangingThePresent.org 上,我们使用分段缓存来缓存首页和其他的几页。所有的这些页均使用了数据库密集访问而且大都是我们最受欢迎的页面。

清单 7. 确定缓存分段

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<% cache 'gifts_index' do %>

    <h3>

      Here, you can make the world a better place with a single gift. Donation gifts

      are also a wonderful way to honor friends and family. Just imagine what we

      can achieve together.

    </h3>

    <h2 class="lightBlue"><%= @event_title %></h2>

    <div id="homefeatureitems">

        <% for gift in @event_gifts %>

          <%= render :partial => 'gifts/listable', :locals => { :gift => gift } %>

        <% end %>

    </div>

    ...

<% end %>


在清单 7 中,cache 帮助程序标识所要缓存的分区。第一个参数是标识此缓存分区的惟一名称。第二个参数包含代码块 — 即第一个 do 和最后一个 end 之间的代码 — 此代码块准确地确定了要缓存的 RHTML 分区。

我们的网站只有一个主页,所以命名这个页面非常容易。在其他地方,我们使用一种特定的方法来决定此网页的 URL 以便惟一标识缓存分段。例如,当我们为特定的内容(比如世界和平或减少贫困)而进行代码缓存时,我们需要使用清单 8 中的代码。代码会为之寻找永久 url,也称为 permalink。

清单 8. 通过 URL 标识缓存分段

1

<% cache @cause.permalink(params[:id]) do %>


通常,当缓存单独页面时,需要用清理器使之过期失效。有时,使用简单的基于时间的对象过期更为容易和简洁。默认地,Rails 并不提供这类机制,但有一种插件名为 timed_fragment_cache 可以实现这一目的。借助这个插件,我可以指定超时,可以在缓存了的内容中指定,也可以在为此页提供了动态数据的控制器代码中指定。例如,清单 9 所示的代码就可以为此页面构建动态数据。when_fragment_expired 方法只有在相关的缓存分段过期时才会执行。此方法接受参数,用来指定超时的时长,它还接受一个代码块,用来指定当内容过期时哪些内容需要重建。我也可以选择在 rhtml 页面中指定超时和缓存方法,但我更愿意使用基于控制器的方法。

清单 9. 基于时间的缓存到期

1

2

3

4

5

def index

  when_fragment_expired 'causes_list', 15.minutes.from_now do

    @causes = Cause.find_all_ordered

  end

end


如果能够容忍数据稍微有些陈旧,那么使用定时的到期机制将可以极大地简化缓存策略。对于每个被缓存的元素,只需指定想要缓存的内容、可生成动态内容的任何控制器动作以及超时。与页面缓存类似,如果需要,也可以使用 expire_fragment :controller => controller, :action => action, :id => id 方法显式让内容到期。此方法的工作方式与缓存动作和缓存页面的到期失效是一样的。接下来,我将介绍如何配置此后端。

Memcached

至此为止,我已经介绍了 Ruby on Rails 的页面和分段缓存模型。看过了 API 之后,现在就可以定义缓存后的数据的去处了。默认地,Rails 将把缓存后的页面放入文件系统。缓存后的页面和动作都会进入公共目录。可以配置缓存后的分段的存储位置。为此,需要用到内存存储、文件系统(在所定义的目录)、数据库或称为 memcached 的服务。对于 ChangingThePresent.org,我们使用 memcached。

可以将 Memcached 想象为一个大型的 hash 图,这个图可通过网络获得。基于内存的缓存速度快,而基于网络的缓存的可伸缩性比较好。有了插件支持,Rails 就可使用 memcached 来缓存分段和 ActiveRecord 模型。要使用它,需要安装 memcached(更多信息,请参看 参考资料)并在 environment.rb(或其他的环境配置文件,比如 production.rb)对它进行配置。

清单 10. 配置缓存

1

2

3

4

5

6

7

8

9

10

11

12

13

14

config.action_controller.perform_caching = true

 

memcache_options = {

  :c_threshold => 10_000,

  :compression => false,

  :debug => false,

  :readonly => false,

  :urlencode => false,

  :ttl => 300,

  :namespace => 'igprod',

  :disabled => false

}

 

CACHE = MemCache.new memcache_options


清单 10 显示了一种典型的配置,其中第一行 config.action_controller.perform_caching = true 将启用缓存。接下来的一行将准备缓存选项。注意,这里的诸多选项是为了让您可以获得更多的调试数据、禁用缓存和定义该缓存的名称空间。在 参考资料 部分给出的 memcached 站点可以找到有关配置选项的更多信息。

模型缓存

我们使用的最后一种缓存是基于模型的缓存。我们使用的是称为 CachedModel 的缓存插件的一种定制版本。模型缓存实际上是一种有限形式的数据库缓存。缓存很容易按模型启用。

要想让模型使用缓存解决方案,只需扩展 CachedModel 类,而非扩展 ActiveRecord,如清单 11 所示。 CachedModel 扩展 ActiveRecord::Base。ActiveRecord 并非全对象关系型映射层。此框架极大地依赖于 SQL 来执行复杂的特性,而且如果需要,用户可以很容易降至 SQL。直接使用 SQL 会使缓存出问题,因为缓存层必须处理完整的结果集,而不是单独一个数据库行。处理完整的结果集常常会问题不断,而且如果没有支持应用程序的深层逻辑,这几乎不太可能。正由于这个原因,CachedModel 的焦点才会放到缓存单个模型对象上,并只加速返回单行结果的查询。

清单 11. 使用 CachedModel

1

Class Cause < CachedModel


大多数 Rails 应用程序都会重复访问多个条目,例如用户对象。模型缓存在很多情况下都可以明显地使速度加快。对于 ChangingThePresent,我们刚刚开始加速基于模型的缓存。

结束语

Ruby 虽然是一门生产率极高的语言,但若从性能角度考虑,该语言解释性的特性让它并不那么理想。大多数主要的 Rails 应用程序都将会通过有效利用缓存来弥补某些不足。对于 ChangingThePresent.org,我们主要使用分段缓存,并通过控制器使用基于时间的方法来使缓存分段到期失效。这种方式很适合我们的网站,即使其中有一些页面会基于登录进来的用户有所变化。

我们还研究了使用受 memcached 支撑的 CachedModel 类所能带来的影响。虽然我们的研究还仅限于缓存对数据库性能所造成的影响,但早期的结果还是很有希望的。在 下一篇 文章中,我将介绍一些实用技巧,您可以使用这些技巧来为另一个真实世界中的 Rails 示例进行数据库优化。

以上是关于Rails的不同缓存策略的主要内容,如果未能解决你的问题,请参考以下文章

如何使控制台中的视图缓存片段过期?

在每个用户的Rails中使用片段缓存

Rails:旧数据与新数据不匹配时如何更新片段缓存

为 memcached 和 Rails 组合片段和对象缓存的最佳方式

Rails - 使用特定属性值缓存所有用户

以下代码片段是不是容易受到 Rails 5 中 SQL 注入的影响?