Webアプリケーションや分散システムでパフォーマンスを高めるために不可欠なのが「キャッシュ」の基本的な活用パターンについて書く。
- キャッシュアサイド(Cache Aside)
- リードスルー(Read Through)
- ライトスルー(Write Through)
- ライトバック(Write Back)
- ライトアラウンド(Write Around)
キャッシュアサイド(Cache Aside)
概要
アプリケーション側が必要に応じてキャッシュを明示的に操作するパターンである。
graph TD
A[アプリケーション] -->|読み込み要求| B[キャッシュ]
B -->|ヒット| A
B -->|ミス| C[データベース]
C -->|データ取得| A
A -->|キャッシュに保存| B
A -->|書き込み| C
A -->|キャッシュ無効化| B
sequenceDiagram
participant A as アプリケーション
participant C as キャッシュ
participant D as データベース
Note over A,D: 読み込み処理(キャッシュミス)
A->>C: データ要求
C->>A: キャッシュミス
A->>D: データ取得
D->>A: データ返却
A->>C: データをキャッシュに保存
Note over A,D: 書き込み処理
A->>D: データ更新
D->>A: 更新完了
A->>C: キャッシュ無効化
読み込み時の流れ
- キャッシュにデータがあるか確認(キャッシュヒット)
- なければDBから取得してキャッシュに保存(キャッシュミス)
書き込み時の流れ
- データベースを更新
- 必要に応じてキャッシュを削除 or 更新
特徴
- キャッシュ管理をアプリケーションが行う
- 読み込み頻度が高く、更新頻度が低いデータに向いている
- キャッシュとDBの整合性はアプリ側の責任である
利用例
- Redis、Memcached等を使ったWebアプリケーション
リードスルー(Read Through)
概要
読み込み時にキャッシュがDBからの取得を自動的に処理するパターンである。アプリケーションはキャッシュのみとやり取りし、キャッシュミス時の処理は透過的に行われる。
graph TD
A[アプリケーション] -->|読み込み要求| B[キャッシュ]
B -->|ヒット| A
B -->|ミス時自動取得| C[データベース]
C -->|データ返却| B
B -->|自動保存後返却| A
A -->|書き込み| C
sequenceDiagram
participant A as アプリケーション
participant C as キャッシュ
participant D as データベース
Note over A,D: 読み込み処理(キャッシュミス)
A->>C: データ要求
C->>D: 自動的にDB取得
D->>C: データ返却
C->>C: 自動キャッシュ保存
C->>A: データ返却
Note over A,D: 書き込み処理
A->>D: データ更新(キャッシュバイパス)
D->>A: 更新完了
読み込み時の流れ
- アプリケーションはキャッシュに読み込み要求
- キャッシュヒット時はそのまま返却
- キャッシュミス時はキャッシュ自身がDBからデータを取得し、自動的にキャッシュに保存してからアプリケーションに返却
特徴
- キャッシュがDBアクセスを透過的に処理する
- アプリケーションはキャッシュの存在を意識しなくて良い
- キャッシュミス時の処理がアプリケーションから隠蔽される
- 書き込みは通常DBに直接行う
利用例
- ORMのL2キャッシュ機能、CDN、プロキシキャッシュ、Hibernate等
ライトスルー(Write Through)
概要
書き込み操作がまずキャッシュに行われ、同時にDBにも書き込む戦略である。
graph TD
A[アプリケーション] -->|書き込み要求| B[キャッシュ]
B -->|同期書き込み| C[データベース]
C -->|完了通知| B
B -->|完了通知| A
A -->|読み込み要求| B
B -->|キャッシュヒット| A
sequenceDiagram
participant A as アプリケーション
participant C as キャッシュ
participant D as データベース
Note over A,D: 書き込み処理
A->>C: データ更新要求
C->>D: 同期書き込み
D->>C: 書き込み完了
C->>A: 更新完了通知
Note over A,D: 読み込み処理
A->>C: データ要求
C->>A: キャッシュヒット(データ返却)
書き込み時の流れ
- キャッシュを更新
- 同じ内容をDBにも即時反映
特徴
- キャッシュとDBが常に整合性を保つ
- 書き込みの遅延はやや大きい
- 読み込みは高速かつ一貫性がある
利用例
- 一貫性が重視されるユーザープロファイル、設定情報、マスターデータなど
ライトバック(Write Back)
概要
書き込み操作はまずキャッシュにのみ反映し、DBへの書き込みは非同期的に遅延処理される。
graph TD
A[アプリケーション] -->|書き込み要求| B[キャッシュ]
B -->|即座に完了通知| A
B -->|非同期でバッチ書き込み| C[データベース]
A -->|読み込み要求| B
B -->|キャッシュヒット| A
D[バックグラウンドプロセス] -->|定期的に| B
D -->|まとめて書き込み| C
sequenceDiagram
participant A as アプリケーション
participant C as キャッシュ
participant D as データベース
participant B as バックグラウンドプロセス
Note over A,D: 書き込み処理(非同期)
A->>C: データ更新要求
C->>A: 即座に完了通知
Note over A,D: 読み込み処理
A->>C: データ要求
C->>A: キャッシュヒット(データ返却)
Note over A,D: バックグラウンド同期
B->>C: ダーティデータ確認
C->>B: 未同期データ返却
B->>D: バッチ書き込み
D->>B: 書き込み完了
書き込み時の流れ
- キャッシュにのみ書き込み(ダーティマーク付与)
- アプリケーションには即座に完了を通知
- 後でバッチ or イベント駆動でDBを更新
特徴
- 書き込みが高速(低レイテンシ)
- クラッシュ時にデータ消失のリスクがある
- 高頻度な更新に向く(同じキーへの連続更新が最終結果だけで済む)
- ダーティデータの管理が重要
利用例
- CPUキャッシュ、ログ、ゲームの一時スコア、計測データなど
ライトアラウンド(Write Around)
概要
書き込み操作をキャッシュに反映せず、DBのみに書き込む戦略である。
graph TD
A[アプリケーション] -->|書き込み要求| C[データベース]
C -->|完了通知| A
A -->|読み込み要求| B[キャッシュ]
B -->|ミス| C
C -->|データ取得| B
B -->|キャッシュに保存後返却| A
B -.->|キャッシュをバイパス| X[書き込み時はスキップ]
sequenceDiagram
participant A as アプリケーション
participant C as キャッシュ
participant D as データベース
Note over A,D: 書き込み処理(キャッシュバイパス)
A->>D: データ更新(キャッシュスキップ)
D->>A: 更新完了
Note over A,D: 読み込み処理(キャッシュミス)
A->>C: データ要求
C->>A: キャッシュミス
A->>D: データ取得
D->>A: データ返却
A->>C: データをキャッシュに保存
書き込み時の流れ
- キャッシュをバイパスしてDBへ直接書き込み
読み込み時の流れ
- 読み込み時にキャッシュミスが発生 → DBから読み出してキャッシュに保存
特徴
- 書き込みがキャッシュを汚染しない(不要なデータをキャッシュに載せない)
- 書いた直後の読み込みでミスが起きやすい(キャッシュに存在しないため)
利用例
- アクセスログ、一時ファイル、バックアップデータなど(低頻度アクセスのレコード)
各パターンの比較表
| 戦略 | 概要 | 読み込み高速 | 書き込み高速 | 整合性 | キャッシュ管理 |
|---|---|---|---|---|---|
| キャッシュアサイド | アプリが明示的にキャッシュ操作 | ◎ | △(管理必要) | △(手動) | アプリが管理 |
| リードスルー | キャッシュが透過的にDB取得 | ◎ | △(DBに直接) | △(読み込み時のみ) | 自動(読み込み) |
| ライトスルー | キャッシュとDBに同時書き込み | ◎ | △(同期待機) | ◎(常に同期) | 自動 |
| ライトバック | キャッシュのみ書き込み後で同期 | ◎ | ◎(非同期) | △(遅延同期) | 自動(リスクあり) |
| ライトアラウンド | 書き込み時はキャッシュバイパス | ◎ | ◎(DB直接) | △(読み込みで整合) | 自動 |
まとめ
どの戦略がベストかは、ユースケースとトレードオフによる。
- 読み込みが主で整合性も重視 → ライトスルー
- 読み込みの透過性を重視 → リードスルー
- 書き込み性能最重視、若干のデータ消失リスクOK → ライトバック
- 低頻度アクセス、キャッシュ効率重視 → ライトアラウンド
- 細かい制御が必要、開発工数をかけられる → キャッシュアサイド
選択時は以下の要素を考慮する:
- 整合性要件: 強整合性が必要かどうか
- 性能要件: 読み込み/書き込みのどちらを重視するか
- 可用性要件: データ消失リスクの許容度
- 運用コスト: 管理の複雑さと開発工数
キャッシュ戦略をうまく使いこなすことで、アプリケーションの性能と可用性は大きく向上する。各プロジェクトの要件に合った選択が重要である。