Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rails の Solid Cache をマスターする:データベースキャッシュによるパフォーマンス最適化の実践ガイド

キャッシングはコンピュータサイエンスにおける重要な概念であり、Webアプリケーション開発にも直結するテーマです。大まかに言えば、キャッシングとは「メモリへのアクセスはデータベースクエリよりも高速で、計算コストも低い」という特性を活かす仕組みです。繰り返し実行される可能性のあるデータベースクエリの結果をキャッシュに保存しておけば、処理フロー全体を大幅に高速化できます。Railsアプリケーションでも例外ではなく、Redisのようなメモリベースのキャッシュを使ってデータを保存し、アプリケーションのパフォーマンスを向上させるのが一般的でした。

Rails の Solid Cache をマスターする:データベースキャッシュによるパフォーマンス最適化の実践ガイド

「データベースをキャッシュとして使う」と聞くと、一見非効率に思えるかもしれません。しかし、Solid Cacheのようなデータベースバックエンドのキャッシュを使うことで、むしろアプリケーションが高速化されるという、直感に反する結果が得られるのです。その理由は、安価なストレージを利用できるため、より多くのデータをより長い期間キャッシュできるからです。Rails 7.1ではSolid Cacheが導入され、データベースバックエンドのアプリケーションキャッシュが手軽な選択肢となりました。

Railsのキャッシュの仕組み

Ruby on Railsにおけるキャッシングは、アプリケーションパフォーマンスの最適化において重要な役割を果たします。開発者がパフォーマンス向上のために利用できるキャッシュには、ページキャッシュ、アクションキャッシュ、フラグメントキャッシュ、ローレベルキャッシュ、SQLキャッシュなど、複数の種類があります。本記事では、その中でもシンプルで、設定可能なcache_storeと互換性のあるローレベルキャッシュを中心に解説します。

Railsフレームワークの中核を担うActiveSupportは、cache_storeを通じてキャッシュとのやり取りや設定を可能にします。config/environments/ディレクトリには環境ごとの設定ファイルがあり、そこでcache_storeを含む各種設定を行います。

cache_storeの設定方法

Redisは非常に人気の高いキャッシュストアであり、業界全体で広く採用されています。本番環境でRedisをキャッシュに使っている場合、config/environments/production.rbには次のような記述があります。

config.cache_store = :redis_cache_store

Redisが使われるのは、メモリキャッシュだからです。メモリは従来、ディスクアクセスやデータベースクエリよりもはるかに高速な読み取り速度を持っています。

Railsでのデータベースクエリのキャッシュ

頻繁に変更されないデータを返す、負荷の高いデータベースクエリの結果を保存するために、ローレベルキャッシュを利用している方も多いでしょう。典型的なコードは以下のようになります。

Rails.cache.fetch("courses", expires_in: 12.hours) do
 Courses.all
end

Redisをキャッシュストアとして使っている場合、このコードはまず過去12時間以内にキー"courses"の値がキャッシュされているかを確認します。存在すれば、ブロック内のコードを実行せずにその値をそのまま返します。キャッシュに値がない場合は「キャッシュミス」と呼ばれ、インタプリタがブロックを実行し、その結果をキャッシュに保存したうえで値を返します。

Solid Cacheとは何か?

前述のキャッシュパターンは、メモリアクセスがAPI呼び出しやデータベースクエリよりも高速であるため、パフォーマンス上のメリットをもたらします。しかし、メモリは高価であり、容量にも限りがあります。そのため、何をどれだけの期間キャッシュするかを最小限に抑えなければなりません。すべてを無期限にキャッシュすることはできず、すぐにメモリが枯渇してしまいます。キャッシングによるパフォーマンス向上とは、突き詰めればトレードオフの問題なのです。時間のかかるタスクの再実行を避けるために、一部のデータを一定期間だけキャッシュするわけです。

Rails 7.1のリリースとともに登場したのがSolid Cacheです。これはSQLデータベースをキャッシュストアとして使用するActiveSupport::Cache::Storeの選択肢です。キャッシュからの読み取りは個々には遅くなりますが、技術の進歩と多くのデータベースに組み込まれたメモリキャッシュのおかげで、その差は十分に許容範囲内に収まっています。

SQLデータベースをキャッシュストアとして使えば、はるかに低コストで何倍ものストレージ容量を確保でき、アプリケーションはより多くのデータをより長い期間キャッシュできます。Basecampチームは、この手法によってアプリケーションが高速化された経緯について記事を公開しており、一見すると直感に反するこの結果が話題になりました。

Solid Cacheはどんな場面で使うべきか?

Redisのようなメモリキャッシュは一般により高速な読み取りを提供しますが、Solid Cacheは以下のようなシナリオで有力な代替手段となります。

大量のデータをキャッシュしたい場合: アプリケーションが大量のデータをキャッシュする必要がある場合、メモリに保存するコストは法外に高くなりがちです。Solid Cacheなら、わずかなコストで桁違いに大きなキャッシュサイズを実現できます。

数ミリ秒の遅延が許容できる場合: キャッシュ取得時に数ミリ秒余分にかかることに対してアプリケーションが敏感でないのであれば、Solid Cacheはパフォーマンスとコストの絶妙なバランスを提供します。潜在的なレイテンシ増加を受け入れられるなら、優れたソリューションといえるでしょう。

Redisインスタンスの管理が現実的でない場合: 別途Redisインスタンスを運用・管理するのが難しい環境では、既存のデータベースでキャッシュを賄えるSolid Cacheの方がデプロイがシンプルになり、Redisのサポートも不要になります。

Solid Cacheのインストール手順

Solid CacheのReadmeには、Railsアプリケーションへの導入手順が詳しく記載されています。まず、Gemfileにgemを追加します。

gem "solid_cache"

次に、Bundlerでインストールします。

bundle install

続いて、データベースがSolid Cacheと連携できるようマイグレーションを追加します。以下のコマンドを実行してください。

bin/rails solid_cache:install:migrations

最後に、生成されたマイグレーションを実行します。

bin/rails db:migrate

RailsアプリへのSolid Cacheの実装

既存のキャッシュをSolid Cacheに置き換える作業は、通常とても簡単です。config/environments/production.rbなどの環境設定ファイルで、cache_storeの設定を以下のように変更するだけです。

config.cache_store = :solid_cache_store

これだけで、Solid Cacheをキャッシュストアとして使い始められます! ActiveSupport::Cache::Storeの他の設定オプションはすべてサポートされており、さらにデータベースシャーディングなど特殊なニーズに対応する追加オプションも用意されています。

なお、この変更は本番環境のキャッシュストアのみを対象としている点に注意してください。他の環境でもテストを行う予定がある場合は、そちらの設定も同様に変更する必要があります。

RailsでSolid Cacheへ移行すべきか?

Rails 7.1でのSolid Cacheの登場は、Railsのキャッシュ戦略における大きな進化を意味します。このソリューションは、Redisのようなメモリベースキャッシュへの伝統的な選好に疑問を投げかけ、アプリケーションパフォーマンス最適化への新たな視点を提供します。

SQLデータベースをキャッシュに活用することで、Solid Cacheはメモリストレージのコストと容量の制約を克服し、より大量のデータを長期間保存することを可能にしました。このアプローチは、効果的にキャッシュできるデータの量を広げるだけでなく、さらなるパフォーマンス向上の可能性も秘めています。

とはいえ、移行するかどうかはアプリケーションの具体的な要件次第です。高速なキャッシングから大きな恩恵を受けており、Redisを支えるインフラが整っているのであれば、Solid Cacheは必ずしも必要ありません。しかし、スタックに新たなサービスを追加することなく、大量のデータをコスト効率よくキャッシュしたいのであれば、Solid Cacheは検討する価値があります。単にRedisの運用負荷を避けたいだけであれば、Solid Cacheはアプリにとって十分すぎるほどの性能を発揮してくれるでしょう。

この記事が役に立った方は、Honeybadgerのニュースレターに登録して、RubyやRailsに関する記事をぜひお見逃しなく。

  1. Rubyのgsubメソッド徹底解説!正規表現・ブロック・ハッシュで使いこなす3つのテクニック

    今回はRubyのgsubメソッドについて、その使い方を詳しく解説していきます。gsubを試すには、まず操作対象となる文字列が必要です。 なぜなら、gsubの目的は文字列の一部を別の文字列に置き換えることだからです。 実は、「gsub」の「sub」は「substitute(置換)」、「g」は「global(全体)」を意味しています。つまり、文字列内に現れる該当箇所をすべて置換するメソッドなのです。 例として、次のような文字列を用意しました: str = white chocolate ここで「white」という単語を「dark」に置き換えたいとしましょう。 その方法がこちらです: str.gs

  2. RubyのRakeとは?タスクランナーの基本と実践的な使い方を解説

    Rakeは、Rubyで最も広く使われているタスクランナー(タスク自動化ツール)です。データベースのバックアップやテスト実行など、プロジェクトで繰り返し行う作業をコマンド一つで実行できるようにしてくれます。 「タスク」とは何か? Rakeで管理する「タスク」の具体例を見てみましょう。 データベースのバックアップを作成する テストスイートを実行する 統計情報を収集してレポートする これらは小さな処理ですが、Rakeがなければプロジェクト内のさまざまなファイルに散らばってしまい、管理が煩雑になります。 Rakeを使えば、こうしたタスクへのアクセスを一元化できます。さらに、特定のパターンに一致し、