Rails 的 cache 介紹一:cache stores
WriterShelf™ is a unique multiple pen name blogging and forum platform. Protect relationships and your privacy. Take your writing in new directions. ** Join WriterShelf**
WriterShelf™ is an open writing platform. The views, information and opinions in this article are those of the author.
Article info
This article is part of:
分類於:
標籤:
日期:
創作於:2019/01/21,最後更新於:2019/02/05。
合計:4855字
Like
or Dislike
About the Author
很久以前就是個「寫程式的」,其實,什麼程式都不熟⋯⋯
就,這會一點點,那會一點點⋯⋯
More to explore
在 Rails ,最讓其他平台使用者攻擊的就是網站執行效率,效率這件事,有很多影響因素,像是 Ruby 的慢就是其中一個重要因素,但是要提高 Rails 的效率,Cache 就是其中很好的方法。
Rails 的 Cache 是建構在 Cache store 上的,Cache store 很好用,很多 gem 也依靠這功能運作,但是也很多細節,它的文件我覺得說得有些難懂,我覺得要搞懂 Rails 的 Cache 要先從底層開始,也就是:
Cache store
Cache store 就是用來存儲 cache 後資料的「店」,沒什麼了不起的,可以把它想像成一個倉庫,把要存的東西,給個標籤,write 進去就好,非常好用簡單,不過,Rails 一下就給你好幾種選擇,所以一開始,就有選擇的挑戰,你就必須要知道這些 store 的差異,這就比較麻煩了,讓我們先從最容易懂的開始:
Cache store: File store
將 cache 資料存成檔案,在 Rails 的官方文件說,預設的 cache store 就是 file store,但是,如果你是用 Rails 5.0+ 以上,你在開發(developement)時,卻可能是另外的 Memory store,或是 Null store (我們等一下介紹這些),這有點討厭,照理來說,開發環境最好是跟運轉的環境一樣,特別是 Memory store 還不支援多工環境,不過,file store 會將資料寫在硬碟目錄中,確實,如果開發環境有一堆亂七八糟資料,也是很討厭的。
依照文件上說,如果你沒有在config.cache_store 設定它的位置,他會寫到 applications root 的 tmp/cache 內。重點來了,原始的 Rails config/environments 中的 development.rb 跟 production.rb 都「沒有預設」,還有一些特別的設定!這,那,我怎麼知道是哪個? 所以首先,確定一下你的 cache store 在那裡,很簡單,到 rails c 中,執行:
Rails.application.config.cache_store
如果你都沒改 config 中設定,我猜你在
如果你的開發環境真的是 null 或是不是跟 production 環境一樣,我的建議就是趕快把 Rails config 在 environments 中的 development.rb 有關於 Rails cache 的設定修改成 file_store,如下,也就是加上 config.cache_store = :file_store, "#{root}/tmp/cache/" 那行,並把原來的 config.cache_store remark :
在 Rails config 中,是用在 tmp 目錄裡有沒有 caching-dev.txt 這個檔案存在,來做開發環境中要不要使用 cache 的切換,他有一個對應的 rails 命令,可以很簡單的切換開發環境,只要在 OS 環境中,執行:
bin/rails dev:cache
你就換看到 cache 的狀態:
這個 rails 命令有一個好處,他除了會刪除或新增 caching-dev.txt 這個檔案,他還會 touch tmp/restart.txt,這會自動重啟你的 rails server,還蠻方便的。
為什麼我的 rails cache 開發環境沒有改變?
如果你執行了
bin/rails dev:cache
但是進入 rails console 後,你再用 Rails.application.config.cache_store 來檢查你目前的 cache store,結果發現竟然還是 :null_store!這時,很有可能你的 config/environments/development.rb 中用來切換 cache 的檔名是錯的,它可能是「mem-caching-dev.txt」,你要將它改為 caching-dev.txt,我目前用的是 Rails 5.0.7,它的預設就是用 mem-caching-dev.txt 檔案來做 config cache 切換,可以確定的是,參考目前 Rails 的 github,是用 caching-dev.txt 為切換 cache 的檔名,這個檔名是對的,畢竟像我這樣要改變 cache store 的人可能不少,檔名用 mem-caching 是有誤導可能。在 config 中,我們也看到 cache control 被設定到 max-age=172800,172800秒也就是兩天,也就是說 Response 的過期時間是 兩天,兩天內重讀同一頁網頁就會被 Cache 住,所以開發時要注意了。還有,rails c 一定要出去再進來,只 reload!是沒用的。 改好了這些後,這時,你再在 rails c 中執行
Rails.application.config.cache_store
你就應該會看到變成 file store ,這跟 production 環境中預設的是一樣了。 如果你的開發環境是用 puma,別用了,趕快改成 file store,Memory store 在多工的環境下,不能用!好了,很高興了,可以來玩 cache store 了,這時如果你看了很多網路上的文章,在 rails c 下做以下的動作:
哈,你會發現 "file_cache_store" 是寫到 applications 的 root, 並不是寫到 tmp/cache 內,這好像跟文件說的不一樣。哈,你搞錯了,在 Rails 中你不需要再宣佈開一家「新的」cache 「店」啦,因爲 Rails 已經幫你開好了,所以,你只要用就好,常用的就是 read,write,fetch 來存讀資料:(不過這也意味著,你可以隨時開一家新店!)
在上面的例子中,這筆 cache 資料被寫到 {applications root}/tmp/cache/062/620/a 中,中間的 062/620 子目錄是隨機產生的,檔名 a 可以確定就是 cache 的標簽 tag。
可是 read 跟 write 並不是 cache 使用上的大咖,fetch 才是!fetch 是個 ruby 指令,基本上,就是讀這個 hash,有就回,沒有時,可以指定回覆值,甚至執行一段指定程式,如下:
Rails.cache.fetch 使用很頻繁,現知道 fetch 怎麼用就好。
使用 File store 就要記得定期清除老資料,不然,它就會一直留在硬碟中,直到,裝滿爆炸(disk full),你可以任意的刪除它,差別就是當這個標簽檔案被刪後,就讀不回原來的資料了,既然是 cache 的資料,所以當然沒差,再建立就有,檔案被刪後,
Rails.cache.read
就會給你一個 nil。有沒有方法設定 rails cache 的 File store 大小呢?好像沒有,用類似 memcache 的設定,如:config.cache_store = :memory_store, { size: 64.megabytes } 不會動,所以,比較好的清除 cache 方式是設定過期(expired)時間,你可以每一個 cache 行為中設定,你也可以設定在 config 中,這樣所有的 cache 就會有一個過期時效:
config.cache_store = :file_store, "#{root}/tmp/cache/", { expires_in: 1.day }
如何清除 File store 所有資料呢?有三個比較常用的方法:
使用上,file store 應該是最方便的,又支援多執行緒的 cache store 了,但是還是有一個很大的限制,就是它跟 Heroku 的 ephemeral filesystem 八字不合,基本上,Heroku 的檔案系統你可以想像成暫存檔,他是隨時會不見的,而且,Heroku 也會每天清掉檔案,如果你是用 Heroku 當 host,那就別想 file store 了。 另外,在 production server 上執行時,要確定是否真的有執行刪除,rake tmp:cache:clear 如果碰到檔案權限問題,就直接裝作沒事,但也沒刪除 cache,Rails.cache.clear 就比較好,會「當」給你看。
Cache store: Memory store
用記憶體存 cache,也就是將 cache 留在記憶體內,這在開發環境中應該是最方便好用的,也是目前 rails 在開發環境中的預設,你可以很簡單的在 config 中設定,如:
config.cache_store = :memory_store, { size: 64.megabytes }
只是,Rails 5 以後,因為新加入的 ActionCable 功能,已經將 Puma 作為 Rails 預設的伺服器,如果你是使用多功( multi-process)的 Phusion Passenger 或是 puma 的 clustered mode,那恭喜你,Memory store 在每一個多功序列中,無法共通,也是因為這原因,所以我前面在介紹 file store 時,就建議直接換店,不要用這個了,另外,Rails console 也因為記憶體不能分享,也不能讀,再加上,我個人喜歡開發環境與運轉越一致越好,眾此種種,我很建議大家就不要用這個 store 了。
Cache store: Memory cache store
這跟 Memory store 是完全不一樣的東西,只有都是用記憶體存資料是一樣的,Memeory cache store 是一個分散式的快取系統,很像是升級版的 memory store,他最大的不同是可以支援多功,據說,這是目前在運轉環境中,使用最廣的,說實話,他真的算是好用又方便。 Rails 的說明寫得好簡單,但是這東西一點都不簡單,想要知道更多什麼是 memeory cache?看這裡: memcached - a distributed memory object caching system: memcached,說故事:這玩意是由 Danga Interactive 很早很早以前(1998?)開發的,開源後,大家覺得實在太好用了,所以也就廣為流傳,只是如今,Danga 早就被賣了幾輪了,有員工還是保留著這家公司的原始連結( Danga 在此 ),各位讀者如果點開網站,下面就列出了當初開發的人現在到那裡去了,開枝散葉,都是高手啊!
哎呦,你說,我就是想用,不想要知道這麼多啦,不行,沒那麼簡單,要用,就請看下去!
Memory cache store 很像網頁運作,有伺服器端、還有用戶端,你要先讓伺服器端跑起來,在 linux 上,一般都伺服器端已經預裝好了,要確定有沒有,就打:
memcached -h
,你就會知道有沒有裝,也會知道版本,memcached 的版本很重要,Rails 的預設用戶端 Dalli gem 要求 1.4 以上,版本不到,就要跟新,詳細的 memcached 安裝,每個作業系統都不一樣,以下是如何在 Mac 上安裝 memcached,其他作業系統就請自己找了。How to Install and Configure Memcached Process/Server on Mac OS X? • Crunchify — Memcached is one of the widely used distributed memory object caching solution out there. I've been using it since last 3 years actively for number of Crunchify
確定有安裝了,簡單啟動 memcached 伺服器端的指令如下:
memcached -l 127.0.0.1 -p 11211 -m 512 -d
這樣,你的 memcached 伺服器端就已經在 127.0.0.1 上執行了,port # 是 11211(大家好像都是用這個 port),佔用 512MB,在背景執行,更多詳細的 memcached 設定,一樣,就打
memcached -h
,就可以看到了。再來就是用戶端了,最簡單知道用戶端能不能讀到 memcached 的資料方法就是:
telnet localhost 11211
連上後,打個 stats,就可以看到 memcached 的狀態了,哈哈哈,你現在知道什麼是 Memory cache store 了,他就是一個用戶端透過 tcpip 存取伺服器端資料的分散式快取系統。那 rails 的用戶端是什麼呢?總不能用 telnet 吧?
這其實是個好問題,照 Rails 的官方說明,是用已經內包的 dalli(達利) gem,但是各位如果查網站,很多的介紹寫的是用 memcache-client gem,會造成混淆,其實這是換過了,2010 年後,全部改成 Dalli,Rails 5 以後已經預設內裝了,早期的版本,還是要安裝,詳細的安裝方式,就看 gem 說明,寫得很清楚:
petergoldstein/dalli — High performance memcached client for Ruby. Contribute to petergoldstein/dalli development by creating an account on GitHub.
GitHub
這些都確定了,使用 Memeory cache store 就很簡單了,在 Rails config/environments 中的 development.rb 跟 production.rb 中加入:
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
就完成了,注意一下,如果你是用多功( multi-process)的 Phusion Passenger 或是 puma 的 clustered mode,那就要另外裝上 gem 'connection_pool',同時也要指定 pool size。
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com", { :pool_size => 5 }
加上 connection_pool 的原因是要避免多執行緒競爭(thread contention,很難翻譯⋯⋯),基本上,只要是多工環境,connection pool 都是要設定的,接下來介紹的 Redis 也是一樣,這是 connetion_pool 的說明:
mperham/connection_pool — Generic connection pooling for Ruby. Contribute to mperham/connection_pool development by creating an account on GitHub.
GitHub
Dalli gem 提供很多設定的選項,像是要不要壓縮資料,連線保留等等,比較特別的是,你可以設定資料存入是 raw,也就是不經過序列化(serialization),這樣可以省下一些容量,你還可以直接 increment 或是 decrement cached 的值,Dalli gem 有很多可以研究跟設定。
Memcached 一旦存滿的,就會自動刪除資料,如果這不是你想要的,你就需要使用 memcached -M 來觸發錯誤,駭客也有可能攻擊沒有登入設定的 memcached 伺服器端,這些是你要注意的。
懶得自己架設 memcached?沒關係,也有很多網路服務商提供這樣的服務,例如:Amazon ElastiCache。
Amazon ElastiCache – 記憶體內資料存放區和快取 — ~
Amazon Web Services, Inc.
簡單嗎?可以算是簡單了,要認知 cache 從來就不是一件簡單的事。。。
用 memcached 效率算是很好的,甚至比接下來要介紹的 Redis 更好,但是目前的趨勢是,大家都開始用 Redis 了,為什麼?最大的好處就是更容易管理跟 scale(擴展)cache 這東東。
Cache store: Redis cache store
Redis 跟 memcached 非常的像,有伺服器端、也一樣有用戶端,你一樣要先讓伺服器端跑起來,他跟 memcached 最像的就是當裝滿的時候,也會自動刪除資料,但是要注意的是,他的預設刪除法並不是依照 Rails 中的 expired 設定,他是走標準的 LRU(Least Recently Used),也就是「最近最少使用」,Redis 4.0 以後,有了一個新的 LFU (Least Frequently Used),這就蠻好用的,rails 手冊建議設定為(allkeys-lfu),詳細的資料看這裡:Using Redis as an LRU cache – Redis。
一般我們用 Redis 來當 cache store 時,設定的讀寫時間越短越好,建議是一秒以下,因爲如果 Redis 反應很慢,那還不如從新再做一頁內容,而且,Rails cache 讀取不會回應說讀不到,不管是 time out 還是沒資料,他就是回覆 nil,也就是沒有這個 cache,所以如果你的 redis 反應很慢,就只是會被當成沒 cache 就是了,還有,Rails 預設也不會自動續連,也就是如果連線斷了,那就沒了,一般不會發生,但是要注意就是了。
好啦,先來看怎麼裝簡易版的 redis 伺服器端,在 Mac 上:
brew install redis
,在 ubuntu 上:sudo apt-get install redis-server
,再來就是要確定他有跑,Mac 上就執行:redis-server /usr/local/etc/redis.conf
,這會跑在前台,你也可以看到 redis 的設定檔就在 /usr/local/etc/redis.conf 內,你可以好好研究要怎麼設定了,在 ubuntu 上,就跟一般 service 一樣:sudo service redis-server start/stop/status
,要記得有執行就是了。接下來就是要安裝 gem 了,如果你是用 rails 5.2 以前的版本,你就要先加上 gem 'redis-rails',5.2 版以後就內建了,一定要裝的就是 gem 'redis',你也可以考慮裝 gem 'hiredis',就把這一或兩個加入 gemfile 中,把 hiredis 想成是一個用 c 寫的加速器,會加快讀去速度,當然,你就不能用 JRuby 了,話說,現在還有人用 Java 嗎?(我只是開玩笑的,別打我),bundle install 後,你就可以用了,你如果已經在用 Rails 的 action cable,redis gem 一定已經裝好了,不過,建議你要將 action cable 的 redis server 跟 cache store 的 redis server 分開來,連 rails api 都強烈建議這樣做了.
ActiveSupport::Cache::RedisCacheStore: Redis cache store. Deployment note: Take care to use a *dedicated Redis cache* rather than pointing this at your existing Redis server.
要測試 Redis 的讀取,很簡單, Redis 有一個很可愛的 command line 介面:redis-cli,在命令頁中打入 redis-cli,然後就可以下個簡單的 info 指令,它就會告訴你很多 redis 的狀況,例如:要知道他用了你多少資源。 不喜歡用 command line?沒關係,因爲你已經裝了 redis gem,你的 rails concole 也可以讀取 redis 了:
如上,1~3 行是讀取 redis 的狀況,你也已經在 4~7 中行看到怎麼從 rails 中去讀寫 redis 了,很簡單對吧!
設定 redis 成 cache store 看這裡,在 Rails config/environments 中的 development.rb 跟 production.rb 中加入以下,要記得改啊,這是我從 rails guide 中直接抄出來的,畢竟,這裡面可是很多學問的。
基本上到此,你就可以用了。
Redis 會自動將記憶體中的資料存入硬碟中,所以,大部分的情況下,你重開機資料也不會掉,當你在開發環境時,如果有必要刪除 redis 的 cache ,就用:
Rails.cache.delete
。Redis 是目前 rails cache 的當紅炸子雞,我的建議是,當網站只有一個主機,流量也不大時,就用 file store 就好了啦,當成長到有需要時,特別是需要兩個以上的主機時,就改用 redis,他一樣有很多網路服務商提供這樣的服務,所以你可以不用自己架設,看你的需求了。
再來就是「沒有」cache store!
Cache store: Null cache store
這是很方便的開發更測試環境設定,事實上,你如果是剛開始用 cache,你目前的開發環境應該就是 nullstore。
在 Rails config/environments 中的 development.rb 跟 production.rb 中加入以下:
這樣,你不管 rails.cache 怎麼讀寫,都是等於沒有 cache hit (中獎)!
介紹完 cache store 了,別忘了,萬事還是要以 rails guide 為準:
Caching with Rails: An Overview — Ruby on Rails Guides
那一個「店」效率好?
這絕對是大家最想要問的問題,標準答案一定是:「沒有一定答案啦,要看應用。」其實不然,如果是老派的電腦程式設計師,一定知道,如果以 IO 的讀寫效率來說:記憶體 》硬碟 》網路,所以 memory store 一定會快於 file store,最慢的一定是 redis 跟 memcached,但是我們還是要考慮你的 server 數目,因為 memory 跟 file store 就只能支援單一 server(file system sharing 就是網路了),所以結論就是:
有一篇文章支援我的論點,各位有空可以看看,他寫的 cache 說明很清楚!是很棒的文章。
Speed Up Your Rails App by 66% - The Complete Guide to Rails Caching: Caching in a Rails app is a little bit like that one friend you sometimes have around for dinner, but should really have around more often.
Rails cache 的使用
寫到這裡,我才發現,這篇真的太長了,所以我就把 Rails cache 的使用寫在另一篇了:
Rails 的 cache 介紹二:網頁 caching — 前一篇文章介紹 cache store,知道了 Rails 的 cache stores 是什麼了後,當然就要知道怎麼用了,網頁 ca...
Scrivinor 思書: 紅寶鐵軌客