9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RailsにおけるCache戦略について(低レベルキャッシュ)

Last updated at Posted at 2019-10-19

ここでは、低レベルキャッシュ(特定の値やクエリ結果をキャッシュする)についてまとめています。

注意: 時間の経過とともにレスポンスが変化するデータのキャッシュについて

時間の経過とともにレスポンスが変わるものをキャッシュする場合は、有効期間(expires_in)を指定して、一定時間後にキャッシュが無効になるようにする必要があります。

キャッシュにはキャッシュキーを指定しますが、レコードが変更された際にこのキーも更新されることで、キャッシュが更新されます。
しかし、例えばCoupon.where('expired_at > ?', Time.current)(有効なクーポンを返す)の場合、レコードの内容は変わらなくても時間の経過とともにレスポンスが変わることが期待されます。有効期限を設定せずにキャッシュを使った場合、レコードの内容が変わらなければキャッシュキーも更新されないため、永久にキャッシュが更新されないという状況になります。
キャッシュの有効期限(expires_in)を設定するようにしましょう。

尚、expires_inは、キャッシュが作られてからの期間です。

はじめに覚えておくこと

  • Rails.cache.fetch このメソッドで、キャッシュのread/writeが両方出来る。
  • <cache_key> 後述の通り、基本的にRailsが用意しているものを使う。
  • expires_in キャッシュの有効期限を指定する。

実装例

Rails.cache.fetch(<cache_key>, expires_in: 3.hours) do
  ...  
  キャッシュが無かった場合、ここで指定した処理が実行され、結果が返されるとともに、
  <cache_key> で指定したキーを使ってキャッシュされる
  ...
end

キャッシュキーの例

cache_key_with_version(単一レコードに対するキー)

  • 以下の通り、「モデルのクラス名」、「id」、「updated_at」を元に生成される。
> coupon.cache_key_with_version
=> "coupons/1-20191011041439715799"
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

cache_key(複数レコードに対するキー)

  • 以下の通り、「モデルのクラス名」、「query-」、「レコード数」、「updated_atの最大値」を元に生成される。
> coupons.cache_key
   (5.9ms)  SELECT COUNT(*) AS "size", MAX("coupons"."updated_at") AS timestamp FROM "coupons"
=> "coupons/query-39c9e75f3c3df8d18398e78bc912b37c-6-20191011041439715799"

## テーブル全体を対象とする場合は以下の通り
> Coupon.all.cache_key
class Product < ApplicationRecord
  def popular_products
    Rails.cache.fetch("#{Product.all.cache_key}", expires_in: 12.hours) do
      <do_something>
    end
  end
end

ログ

キャッシュ・ライト時のログ

Cache write: channels/query-cce0a1bf6d4900cec9cf496202394b05-1-20191016105530000000
   (1.5ms)  SELECT COUNT(*) AS `size`, MAX(`catalogs`.`updated_at`) AS timestamp FROM `catalogs`
  ↳ app/controllers/toppage_controller.rb:27

キャッシュ・リード(キャッシュ・ヒット)時のログ

Cache read: catalogs/query-5bef3b48a20fab8e12bcfb3f222f834d-1-20191004025349000000
Cache fetch_hit: catalogs/query-5bef3b48a20fab8e12bcfb3f222f834d-1-20191004025349000000

備考

キャッシュはデフォルトではproduction環境でのみ有効です。
ローカルでキャッシュを使う場合は、対応するconfig/environments/*.rbファイルでconfig.action_controller.perform_cachingtrueに設定する必要があります。
但し、ここで説明した「低レベルキャッシュ」の動作には影響しません。

config.action_controller.perform_caching値の変更は、Action Controllerコンポーネントで提供されるキャッシュでのみ有効になります。つまり、後述する 低レベルキャッシュ の動作には影響しません。

railsguide.jpより。

9
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?