Redisでキャッシュ高速化入門 — DB負荷を減らす仕組み
本記事はアフィリエイト広告(A8.net)を含みます。価格・仕様は2026年6月時点の目安です。最新は各公式サイトでご確認ください。
TL;DR
- DB への繰り返しクエリが遅さの主因であることが多い。Redis をキャッシュ層として挟むことで、同じデータへの2回目以降のアクセスをメモリから返せる。
- Redis は インメモリ KV ストア。
SET/GET/EXPIREの3コマンドが使い方の基礎になる。 - キャッシュ戦略は Cache-Aside(読み時にキャッシュ) が最も一般的。書き込み時に該当キャッシュを失効させる(TTL または手動 DEL)とデータの整合性を保ちやすい。
- Redis のデータはデフォルトでは揮発性。永続化が必要なら RDB(スナップショット)または AOF(追記ログ)を設定する。
- メモリ上限(
maxmemory)と eviction ポリシー(allkeys-lruなど)を設定しないと、メモリが枯渇してサーバーごと落ちる可能性がある。 - 注意点は2つ: キャッシュの一貫性(更新タイミング)と Cache Stampede(一斉再生成の競合)。
なぜキャッシュ層が必要になるのか
Web アプリが遅くなる原因の多くは DB クエリの繰り返し実行です。例えばトップページを表示するたびに「直近30件の記事一覧を取得する SELECT」が走るとすると、ユーザーが増えるほど DB への同じクエリが増え続けます。
DB はディスク(またはバッファプール)からデータを読み出す処理が必要なため、アクセス集中時にはレイテンシが跳ね上がります。キャッシュ層はこの問題を「一度取得した結果をメモリに置き、次のリクエストはそこから返す」という方法で解決します。
クライアント → アプリ → Redis(キャッシュ HIT) → レスポンス
↓ キャッシュ MISS のとき
PostgreSQL → Redis に保存 → レスポンス
DB に届くクエリが減れば、DB 自体のレイテンシも改善します。トレードオフは「最新のデータと少しズレる可能性がある」こと。これをどこまで許容するかが設計の核になります。
DB のバックアップ・リストア手順は PostgreSQL/MySQL バックアップ・リストアガイド で扱っています。
Redis とは
Redis(Remote Dictionary Server)は、データをすべてメモリ上に保持する KV(キー・バリュー)型のデータストアです。2009年に Salvatore Sanfilippo が開発を始め、現在は Redis Ltd. が主導しています(2024年以降は SSPL ライセンスに移行)。
用途として広く使われているのは次の4つです。
| 用途 | 概要 |
|---|---|
| キャッシュ | DBクエリ結果やHTMLフラグメントを一時保存し再利用する |
| セッション管理 | ユーザーのセッション情報をメモリに保持する |
| レート制限 | 単位時間あたりのリクエスト数をカウントして制御する |
| キュー / Pub/Sub | 非同期タスクの積み上げ・ワーカーへの配信 |
メモリ上で動作するため、読み書きはマイクロ秒オーダーで完了します。一方、メモリはディスクより高価で容量も限られているため、Redis に格納するデータは「失っても再生成できるもの」に限定するのが基本です。
インストールと起動確認
Ubuntu / Debian 系の VPS では次のコマンドで Redis をインストールできます。
sudo apt update
sudo apt install -y redis-server
インストール後、サービスを起動して動作を確認します。
sudo systemctl enable --now redis-server
redis-cli ping
PONG が返れば Redis は正常に動作しています。redis-cli はデフォルトで 127.0.0.1:6379 に接続します。
Docker Compose で動かす場合は、Docker Compose で自己ホストを始める の構成を参考に以下のサービス定義を追加します。
services:
redis:
image: redis:7.4
container_name: myapp-redis
restart: unless-stopped
ports:
- "127.0.0.1:6379:6379"
volumes:
- redisdata:/data
command: redis-server --save 60 1 --loglevel warning
volumes:
redisdata:
ports のバインドを 127.0.0.1:6379:6379 にしているのは、外部から直接 Redis に接続されないようにするためです。デフォルトで認証なしで接続できるため、パブリック IP に開けることは避けてください。
基本コマンド
redis-cli を起動して基本的なコマンドを確認します。
redis-cli
SET / GET
127.0.0.1:6379> SET mykey "hello"
OK
127.0.0.1:6379> GET mykey
"hello"
SET でキーと値を保存し、GET で取得します。値はデフォルトでは文字列(bytes)として扱われます。
EXPIRE / TTL
127.0.0.1:6379> SET session:user:42 "abc123"
OK
127.0.0.1:6379> EXPIRE session:user:42 3600
(integer) 1
127.0.0.1:6379> TTL session:user:42
(integer) 3598
EXPIRE で有効期限(秒)を設定します。TTL で残り秒数を確認できます。-1 は無期限、-2 はキーが存在しないことを意味します。
SET と同時に TTL を指定するには EX オプションが使えます。
127.0.0.1:6379> SET cache:articles:list "[...]" EX 300
OK
これは「300秒後に自動削除する」というキャッシュの典型的な使い方です。
DEL / EXISTS
127.0.0.1:6379> DEL mykey
(integer) 1
127.0.0.1:6379> EXISTS mykey
(integer) 0
DEL でキーを削除します。キャッシュを手動で無効化(invalidate)するときに使います。EXISTS は存在確認で 1(あり)または 0(なし)を返します。
KEYS と SCAN
127.0.0.1:6379> KEYS cache:*
KEYS はパターンに合うキーを全列挙します。ただし、キー数が多い本番環境で KEYS * を実行するとサーバーがブロックするため、代わりに SCAN を使います。
127.0.0.1:6379> SCAN 0 MATCH cache:* COUNT 100
SCAN はカーソルを進めながら少量ずつ取得するため、大規模な環境でも安全に使えます。
INCR / DECR
127.0.0.1:6379> SET rate:user:42 0
OK
127.0.0.1:6379> INCR rate:user:42
(integer) 1
127.0.0.1:6379> INCR rate:user:42
(integer) 2
INCR はアトミックなカウントアップです。レート制限の実装でよく使われます。
キャッシュ戦略
Cache-Aside(読み時にキャッシュ)
最も広く使われるパターンです。アプリ側で次の順序を実装します。
- Redis にキーが存在するか確認(
GET) - HIT(データあり)なら Redis の値をそのまま返す
- MISS(データなし)なら DB からクエリして取得する
- 取得した結果を Redis に保存(
SET ... EX TTL)して返す
疑似コードで示します。
def get_articles():
cached = redis.get("cache:articles:list")
if cached:
return json.loads(cached) # HIT: Redis から返す
rows = db.query("SELECT * FROM articles ORDER BY pub_date DESC LIMIT 30")
redis.set("cache:articles:list", json.dumps(rows), ex=300) # 5分キャッシュ
return rows
Redis への依存を低く保てる(Redis が落ちても DB から取得できる)点が Cache-Aside の利点です。
書き込み時の失効
DB を更新したとき、古いキャッシュを残したままにすると「DB と Redis の値が違う」状態が生まれます。対策は2つです。
| 方法 | 手順 |
|---|---|
| TTL による自動失効 | EX で有効期限を設定し、一定時間後に自動削除させる |
| 手動 DEL | DB 更新と同時に DEL cache:articles:list を呼び出して明示的に削除する |
TTL だけに頼ると「TTL が切れるまでの間は古いデータを返し続ける」ことを受け入れる必要があります。更新頻度が低いデータ(設定値・カテゴリ一覧など)は TTL だけで十分なことが多く、更新頻度が高いデータや整合性が重要なデータは手動 DEL と組み合わせます。
揮発性と永続化
Redis はデフォルトでメモリ上にのみデータを持ちます。プロセスが停止するとデータは消えます。永続化が必要な場合は次の2つの仕組みを使います。
| 方式 | 概要 | 特徴 |
|---|---|---|
| RDB(スナップショット) | 一定間隔でメモリの状態をバイナリファイル(dump.rdb)に書き出す | ファイルが小さく復元が速い。インターバル分のデータが失われうる |
| AOF(Append Only File) | 書き込み操作をすべてログファイル(appendonly.aof)に追記する | データ損失を最小化できる。ファイルが大きくなる。定期的な rewrite が必要 |
キャッシュ用途(失ってもDBから再生成できる)であれば、永続化は不要か RDB のみで十分なことが多いです。セッション管理など「失うと影響が大きい」データを Redis に持たせる場合は AOF を有効化します。
redis.conf の主な設定項目です。
# RDB: 60秒以内に1件以上変更があったらスナップショットを保存
save 60 1
# AOF を有効にする
appendonly yes
appendfsync everysec # 毎秒 fsync (パフォーマンスと安全性のバランス)
Docker Compose での設定例は前述の command 行(--save 60 1)が RDB の設定に相当します。
メモリ上限と eviction ポリシー
Redis にメモリ上限を設定しないと、データが増え続けてサーバーのメモリを使い切り、OOM(Out of Memory)でプロセスが強制終了します。本番運用では必ず maxmemory を設定します。
# redis.conf または redis-cli で設定
maxmemory 256mb
maxmemory-policy allkeys-lru
maxmemory-policy は上限に達したとき何を削除するかのルールです。
| ポリシー | 動作 |
|---|---|
noeviction | 削除しない。書き込みエラーを返す(デフォルト) |
allkeys-lru | 全キーの中から最近使われていないものから削除する |
allkeys-lfu | 全キーの中からアクセス頻度が低いものから削除する |
volatile-lru | TTL 付きキーの中から最近使われていないものを削除する |
allkeys-random | 全キーの中からランダムに削除する |
キャッシュ用途では allkeys-lru が最も一般的な選択です。セッション等の重要なデータと混在させている場合は volatile-lru(TTL 付きのキャッシュだけを削除対象にする)を検討します。
現在の設定と使用状況は次のコマンドで確認できます。
redis-cli INFO memory
used_memory_human と maxmemory_human を見て余裕を管理します。
注意点
キャッシュの一貫性
DB とキャッシュは別々のストアです。「DB を更新したが Redis は古いままだった」という状態は必ず起きます。完全な一貫性が必要な箇所にキャッシュを挟む場合は、更新時の DEL を必ずセットで実装します。また、金融取引など整合性が最優先の処理はキャッシュを介さず DB に直接クエリするのが安全です。
Cache Stampede(キャッシュスタンピード)
人気コンテンツのキャッシュ TTL が切れた瞬間、大量のリクエストが同時に DB クエリを発行する現象です。DB に急激な負荷がかかり、最悪の場合は DB が応答できなくなります。
対策の代表例はMutex Lock(ロック付き再生成)です。最初の1リクエストだけが DB クエリを実行し、結果を Redis に書き込む。その間の他リクエストは短時間待機してから Redis を再確認する、というパターンで競合を防ぎます。また、TTL を固定値でなく「基準値 ± ランダムな秒数」にすることで、複数のキャッシュが同時に切れる状況を分散させる方法もよく使われます。
まとめ
| ポイント | 要点 |
|---|---|
| なぜキャッシュが必要か | 同じ DB クエリの繰り返しを減らしレイテンシとDB負荷を下げる |
| Redis の立ち位置 | インメモリ KV ストア。キャッシュ・セッション・レート制限・キューに使われる |
| 基本コマンド | SET / GET / EXPIRE / TTL / DEL / SCAN / INCR |
| キャッシュ戦略 | Cache-Aside が基本。DB 更新時は TTL 自動失効または手動 DEL で整合性を保つ |
| 永続化 | キャッシュ用途は RDB のみで十分なことが多い。セッション等は AOF を検討 |
| メモリ管理 | maxmemory と maxmemory-policy(allkeys-lru)を必ず設定する |
| 注意点 | キャッシュの一貫性と Cache Stampede の2点を押さえておく |
キャッシュ層の導入で「DB を変えずにレスポンスが速くなる」体験は、最初に触れると効果が分かりやすいです。まずは読み取り頻度が高くて更新が少ないデータ(トップページの一覧・設定値など)を1つ Redis に乗せてみることから始めてください。
各サービス公式
VPS の契約は各公式サイトから行えます。