天天看点

Rails caching(Rails高速缓存)1. 基本缓存2 cache stores3. caching in development

目录

1. 基本缓存

1.1 page caching

1.2 action caching

1.3 fragment caching

1.4 俄罗斯套娃caching

1.5 共享部分caching

1.6 管理依赖

1.7 低级别caching

1.8 sql caching

2 cache stores

2.1 configuration

2.2 activesupport::cache::store

2.2.1 connection pool options

2.2.2 定义化cache stores

2.3 activesupport::cache::memorystore

2.4 activesupport::cache::filestore

2.5 activesupport::cache::memcachestore

2.6 activesupport::cache::rediscachestore

2.7 activesupport::cache::nuillstore

3. caching in development

本文将介绍如何通过高速缓存机制来加速Rails程序应用。

高速缓存意味着在request-response这个cycle里生成存储数据,然后在反馈相似的请求时复用。

缓存机制是提高应用性能的最高效的机制。通过缓存机制,连接单数据库的单服务器能够支持千级并发。

Rails提供了一系列的开箱即用的caching特性。 本手册将介绍介绍每个特性的范围和目的。

通过阅读本文,你会学到:

  • 片段和俄罗斯套娃缓存
  • 怎么解决缓存依赖
  • 备用缓存存储
  • 条件Get支撑

1. 基本缓存

本段将介绍三种类型的缓存技术: page, action和fragment缓存。默认,Rails提供了片段缓存。为了使用page和action的caching机制,需要在Gemfile中添加actionpack-page_caching和actionpack-action_caching。

默认,caching只在production环境中使用。如果想在本地使用缓存机制,就需要在本地config/environments/*.rb文件中配置config.action_controller.perform_caching=true。这个值的改变仅会影响

Action Controller中的caching,而不会影响到实例中的低级别的caching。

1.1 page caching

page caching允许请求生成一个web page的请求。由于这是特别块的,所以其并不适用于每一个情况,例如认证。这时候的访问就像从一个文件系统直接获取文件,则为了安全期间,我们需要设置cache的失效期。

需要注意的是, page caching已经从Rails 4.0中移除。

1.2 action caching

如上段所说,page caching不能用于filter之前的actions。例如,需要认证的pages,这就是为什么会有action caching。

需要注意的是,action caching也已经从Rails 4.0中移除。

1.3 fragment caching

动态的web应用通常会基于许多组件(存储机制不同)来生成pages。当page中不同部分需要cached,并且需要不同的生命周期时,则需要使用fragment caching。

fragment caching允许view的段落可以被包装成一个cache block然后当下一个请求来的时候从cache store中取出来。

例如,可以用以下代码来cache页面中的每一个product:

<% @products.each do |product| %>
  <% cache product do %>
    <%= render product %>
  <% end %>
<% end %>
           

当你的应用收到关于这个界面的第一个请求时,Rails会基于唯一键写一个新的cache,例如

views/products/index:bea67108094918eeba42cd4a6e786901/products/1
           

这个中间的hash字符是基于我们caching的view片段的内容计算出来的。如果这个view片段发生改变,hash字符都会改变,老的cache则会失效,且会从cache stores里删除。

如果想基于某个条件cache某个片段,则可以使用‘cache_if’或者'cache_unless'。

<% cache_if admin?, product do %>
  <%= render product %>
<% end %>
           

除了单个cache,也可以集合方式cache,如下:

<%= render partial: 'products/product', collection: @products, cached: true %>
           

1.4 俄罗斯套娃caching

cache也是可以嵌套的,这叫做俄罗斯套娃caching。

俄罗斯套娃caching的优势在于:如果单一产品更新了,其他内嵌的片段可以被重复使用用于生成外部的片段。

当一个cached文件的updated_at变化了,则这个cache就失效了,但是。其内部镶嵌的片段都却不会过期。如下片段:

<% cache product do %>
  <%= render product.games %>
<% end %>
           

当game的任一属性改变了,则updated_at会设置成当前时间,然后game的cache会过期。然而,相关联的product会不会因此而过期,从而产生陈旧数据,这是不正确的。

则为了解决这个问题, 我们需要用touch方法将这两个主题连接起来。

class Product < ApplicationRecord
  has_many :games
end

class Game < ApplicationRecord
  belongs_to :product, touch: true
end
           

这样的话,任何game的变化都会引起相关联的product的cache的更新。

1.5 共享部分caching

共享部分cachings也是有可能实现的。例如,共享部分caching允许模版在html和javascripts文件件共享部分文件。

1.6 管理依赖

为了正确的废止cache,我们需要正确定义caching依赖。Rails默认能处理一般例子,然而,有时,当你处理定制化的helpers时,则需要显示定义他们。

隐式依赖定义:

render @project.documents.where(published: true)
           

显示依赖定义:

<%# Template Collection: notification %>
<% my_helper_that_calls_cache(some_arg, notification) do %>
  <%= notification.name %>
<% end %>
           

1.7 低级别caching

有时,我们不需要去cocah view的片段,而仅仅需cache一个值或者query的结果。幸运的是,rails的caching机制支持保存任何信息。

最高效的实现底层caching则需要用rails.cache.fetch方法。这个方法支持读写cache。当参数是一个值时,则key被获取,caches里的值被返回。如果参数是一个块,则没有cache时,该代码块会被执行,然后返回值会被写进cache。

如下例子:

class Product < ApplicationRecord
  def competing_price
    Rails.cache.fetch("#{cache_key_with_version}/competing_price", expires_in: 12.hours) do
      Competitor::API.find_price(id)
    end
  end
end
           

1.8 sql caching

query caching缓存每一个query返回的查找结果。如果rails遇到相同的query,则会使用cached的值,而不会再次查询。

例如:

class ProductsController < ApplicationController

  def index
    # Run a find query
    @products = Product.all

    ...

    # Run the same query again
    @products = Product.all
  end

end
           

这二次去数据库执行相同的查询,但是并不是去真正的访问数据库。第一次查询返回的结果存储在query cache里面(内存里),第二次会直接从内存中获取到。

然而,查询caches在一个action开始的时候创建,在action结束的时候销毁,所以仅仅存在在一个action的生命周期里。如果你希望这个查询结果保存的更长久,则可以使用low-level caching。

2 cache stores

除了sql和page caching,rails为不同的存储数据存储了不同的存储。

2.1 configuration

你可以通过设置config.cache_store配置选项来配置应用程序默认的cache store。如下:

config.cache_store = :memory_store, { size: 64.megabytes }
           

除了这种方法,你还可以在configuration块外调用actioncontroller::base.cache_store去定义cache store.

还可以通过rails cache去获取cache。

2.2 activesupport::cache::store

这个类提供了rails中cache交互基础。这个是抽象类,我们不能直接使用。我们在具体化类时需要和存储引擎绑定。

主要方法包括:read, write, delete, exist?和 fetch。

2.2.1 connection pool options

默认的,

MemCacheStore和RedisCacheStore使用一个单一的连接进程。如果想增加可用连接的数字,则使能connection pooling。

第一步,在Gemfile文件中添加connection_pool 

gem 'connection_pool'
           

然后,在配置cache store时陪你pool_size和pool_timeout,如下:

config.cache_store = :mem_cache_store, "cache.example.com", { pool_size: 5, pool_timeout: 5 }
           

2.2.2 定义化cache stores

我们可以通过继承activesupport::cache::store来创建自己的cache store。如下实例化类:

config.cache_store = MyCacheStore.new
           

2.3 activesupport::cache::memorystore

这个cache stor保存在同一进程中的实例在内存中。我们可以通过以下配置类初始化cache store。默认大小为32m,当所占的内存超过该值时,则使用最少的一些值会被清除。

config.cache_store = :memory_store, { size: 64.megabytes }
           

如果rails应用多程序运行譬如用了phusion passenger,则rails server process实例间无法共享cache 数据。这种cache store对大型应用的部署并不适用。而对于small, low traffic的网站,却能很好的适用。

由于当我们使用:memory_store,进程间无法共享进程,所以我们无法通过rails console去手动的读写或者失效cache。

2.4 activesupport::cache::filestore

这种cache store用文件系统去存储实体。如下:

config.cache_store = :file_store, "/path/to/cache/directory"
           

如果我们没有如上设定位置,则cache默认会存储在“#{root}/tmp/cache”目录下

2.5 activesupport::cache::memcachestore

这个cache store用danga's memcached 服务器为服务提供了中心化的缓存。rails默认用了dundled dalli gem。这是目前对production网站最流行的cache store。他可以用来提供单一的,共享的高性能高冗余的cache集群。

当初始化cache时,你需要指定集群中memcached servers的地址,如下:

config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
           

如果不指定地址,则默认为本地(如下),对于小网站,这样是可以的,但是对于大型应用是不可以的。、

config.cache_store = :mem_cache_store # Will fallback to $MEMCACHE_SERVERS, then 127.0.0.1:11211
           

2.6 activesupport::cache::rediscachestore

redis cache store的优势是当达到最大存储空间会自动清理。我们需要在gemfile中添加redis gem。如下:

gem 'redis'
           

我们可以使能更快的hiredis链接库。我们需要在gemfile中添加hiredis gem。如下:

gem 'hiredis'
           

redis cache store会自动需要和使用hiredis。而不需要其他更多额外的配置。

2.7 activesupport::cache::nuillstore

这个cache store的实现意味着其仅在development和test环境中使用,且不存储任何东西。这在开发时是很有用的,你可以通过rails.cache直接看到代码的变化。

config.cache_store = :null_store
           

3. caching in development

我们说了通常cache只在production环境中有效,当我们想在开发环境中测试我们的cache机制时,rails提供了dev:cache去方便的打开和关闭caching。如下:

$ bin/rails dev:cache
Development mode is now being cached.
$ bin/rails dev:cache
Development mode is no longer being cached.
           

继续阅读