キャッシュと言っても種類が多い
Webシステムの開発や運用において「とりあえずキャッシュを入れて負荷を下げよう」となりがちですが、キャッシュと一言で言ってもその種類や特性は様々です。
それぞれの特徴、メリット・デメリット、そして導入時の注意点について整理してみました。
1. ブラウザキャッシュ
メインDBの負荷軽減:×
表示速度UP:○
ヒット率:×
実装工数:○(低い=すぐできる)
コスト:○(低い=追加費用なし)
ユーザー個々のブラウザ(クライアント側)にデータを保存させるキャッシュです。
同じユーザー・同じブラウザ内での再訪問でしか効果を発揮しないため、システム全体としてのヒット率はかなり低くなります。
一方で、サーバー側のコストも実装工数もほとんどかからないため、念のために画像やJS、CSSなどの静的ファイルをブラウザでキャッシュさせる設定にしているサイトが多いです。
2. CDN(Content Delivery Network)
メインDBの負荷軽減:○
表示速度UP:○
ヒット率:○
実装工数:×(高い=考慮事項が多い)
コスト:×(高い=サービス利用料が発生)
Akamaiなどが有名なサービスで、メインDBやWebサーバーの負荷軽減、表示速度UP、ヒット率の高さにおいて非常に優秀です。
しかし、ページ全体をそのままキャッシュしてしまうため、そのページ内でお気に入り登録などの動的な機能が使えなくなってしまうという注意点があります。
そのため、ログイン済みユーザーにはキャッシュを返さない(バイパスする)といった出し分けの設定を行っている現場が多いです。
3. テンプレートキャッシュ
メインDBの負荷軽減:○
表示速度UP:○
ヒット率:△
実装工数:○
コスト:○
Smartyの template_c などがわかりやすい例です。
DBから取得したデータを保持しておき、次回からはそのデータを使ってレンダリングをスキップ(または高速化)することでメインDBの負荷を下げられます。
DBから取得した配列データをJSONにしてキャッシュするよりも、テンプレートキャッシュにした方がHTMLのレンダリング処理の分だけ表示速度が速くなります。
ただし、Webサーバーが複数台あるマルチサーバー環境では、サーバー間でキャッシュが共有されず、キャッシュのヒット率が下がってしまうなどのデメリットがあります。
4. Redis(Memcached)でキャッシュ
メインDBの負荷軽減:○
表示速度UP:○
ヒット率:○
実装工数:×(設計・実装の考慮が必要)
コスト:△
負荷がかかりそうな処理に対して、RedisやMemcachedなどのインメモリDBにキャッシュして負荷を軽減する構成は一般的です。
ただ、いくらRedisを導入しても実装方法が間違っていると大きな表示速度UPは望めません。
例えば、商品情報をキャッシュしたとして、100商品を見せる一覧ページで「100回のRedisリクエスト」が発生するような実装(いわゆるN+1問題)になっているケースです。
これではメインDBの負荷を下げられたとしても、ネットワーク往復のオーバーヘッドによりスピードUPには繋がりません。
この場合は、一覧の表示条件に合わせて100商品分のデータを1つのキャッシュにまとめるか、MGET やパイプライン処理を用いて一括取得する工夫が必要です。
ただし、一覧ページは商品の更新頻度によってもキャッシュの向き不向きがあり、一概には決められない難しさがあります。
5. RDBMSのクエリキャッシュ
メインDBの負荷軽減:△
表示速度UP:○
ヒット率:△
実装工数:○
コスト:×
以前のRDBMSには重いSQLクエリの結果をそのままキャッシュする機能があり、それに助けられるケースもありました。
しかし、MySQL 8.0でクエリキャッシュ機能が完全に廃止されたことからも分かるように、更新時のロック競合やメモリ圧迫の原因になりやすいため、現代のDB設計においてクエリキャッシュを頼りにするのは避けるべきです。
基本的には、適切なインデックス設計、クエリの最適化、DBのメモリ設定を行うことが前提となります。
まとめ:キャッシュ導入の難しさと構成の見直し
CDNやRedisを使っても、動的なページではキャッシュできる範囲や有効期限(TTL)に制限がかかるため、綺麗な設計を行うのはなかなか難しいのが実情です。
結果として、保存先を別の場所に移してメインDBの負荷を一時的に逃がしただけになっていたり、費用対効果をしっかり検証せずに「なんとなく」キャッシュを導入している現場も少なくありません。
また、キャッシュを導入すると、データの更新時に古いキャッシュを破棄する処理(キャッシュインバリデーション)が必要になり、開発とテストの難易度・工数が大幅に上がってしまいます。
もし、高度なトランザクション処理や複雑なリレーションがそれほど必要なく、単純な読み書きが多いシステムであれば、安易にキャッシュ層を追加して構成を複雑にするのではなく、最初からインデックスをメモリに乗せるようにテーブルを別だしするなどのアプローチも有効な選択肢です。
システム要件に合っていれば、設計をシンプルに保ちつつ、スピーディでコストを抑えたWebシステムを構築できる可能性があります。