目錄
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.