Redis(および互換実装の Valkey など)を本番で安全・高速・低コストに運用するための実践ガイドです。


目次

  1. キー設計
  2. データ型の選び方
  3. メモリ管理
  4. TTL と有効期限
  5. パフォーマンス
  6. 避けるべきコマンド・アンチパターン
  7. キャッシュ戦略
  8. 永続化
  9. 高可用性とスケーリング
  10. セキュリティ
  11. クライアント・接続管理
  12. 監視・運用
  13. チェックリスト

1. キー設計

命名規則を統一する

コロン区切りの階層構造が一般的です。

<アプリ/サービス>:<エンティティ>:<ID>:<属性>
例) shop:user:1001:profile
    shop:cart:1001
    shop:session:ab12cd34
  • 一貫性: チーム全体で命名ルールを文書化する
  • 適度な短さ: キー名もメモリを消費する。数億キーの規模では user: → u: のような短縮も検討(可読性とのトレードオフ)
  • バージョン付与: スキーマ変更に備えて v2:user:1001 のようにプレフィックスでバージョン管理すると移行が楽

ホットキーを避ける

1つのキーにアクセスが集中すると、Cluster 環境では特定ノードがボトルネックになります。

  • カウンタは counter:{id}:shard:N のように分割し、読み取り時に合算する
  • 読み取り集中ならアプリ側のローカルキャッシュ(クライアントサイドキャッシュ)を併用する

ビッグキーを避ける

  • 目安: String は 10KB 以下、コレクション型は 要素数 数千〜1万以下 に抑える
  • 巨大な Hash / List / Set / ZSet は削除・取得時にメインスレッドをブロックする
  • 大きなデータは分割する(例: user:1001:followers:0, ...:1 …)

Cluster のハッシュタグ

複数キー操作(MGET、トランザクション、Lua)を Cluster で行う場合、{} で同一スロットに寄せます。

{user:1001}:profile
{user:1001}:cart

ただし乱用するとスロットが偏るので、必要な範囲に限定します。


2. データ型の選び方

用途推奨データ型備考
単純なキャッシュ・カウンタStringINCR / INCRBY はアトミック
オブジェクト(複数フィールド)Hash小さい Hash はメモリ効率の良いエンコーディングになる
キュー(簡易)ListLPUSH + BRPOP
信頼性のあるキュー / イベントログStreamコンシューマグループ・ACK・再配信が可能
ユニークな集合Set和・積・差集合
ランキング・優先度付きキュー・時系列インデックスSorted Setスコア順の範囲取得が O(log N + M)
ユニーク数の概算HyperLogLog約 12KB で誤差 ~0.81%
フラグ・日次アクティブ判定Bitmap1ユーザー 1bit
位置情報Geo内部は Sorted Set
構造化ドキュメントJSON(モジュール/新しい版では標準搭載)部分更新が可能

ポイント

  • JSON 文字列を String に丸ごと入れるより Hash の方が有利な場合が多い(部分取得・部分更新が可能)
  • 小さな Hash を多数作ると、コンパクトなエンコーディング(listpack)が効いてメモリ効率が上がる
  • Pub/Sub は「撃ちっぱなし」で、購読者がいない間のメッセージは失われる。取りこぼしが許されないなら Stream を使う

3. メモリ管理

maxmemory を必ず設定する

maxmemory 4gb
maxmemory-policy allkeys-lru
  • 物理メモリの全量を割り当てない。fork(RDB/AOF書き換え)時の Copy-on-Write やバッファ分の余裕として、物理メモリの 60〜75% 程度を目安にする

退避ポリシー(maxmemory-policy)の選択

ポリシー適した用途
allkeys-lru / allkeys-lfu純粋なキャッシュ用途(最も一般的)
volatile-lru / volatile-lfuキャッシュと永続データが混在(TTL付きキーのみ削除)
volatile-ttl期限が近いものから消したい場合
noevictionデータを失ってはいけない用途(書き込みがエラーになる)
  • アクセス頻度に偏りがあるなら LFU の方がヒット率が高くなりやすい
  • キャッシュと永続データ(セッション・キュー等)は別インスタンスに分けるのが理想

メモリの確認

INFO memory              # used_memory, mem_fragmentation_ratio など
MEMORY USAGE <key>       # キー単位の使用量
MEMORY DOCTOR            # 診断
redis-cli --bigkeys      # 大きなキーの検出
redis-cli --memkeys      # メモリ使用量の大きいキー
  • mem_fragmentation_ratio が 1.5 を大きく超える場合は activedefrag yes を検討
  • 1.0 を下回る場合はスワップが発生している可能性がある(要対処)

4. TTL と有効期限

  • キャッシュには必ず TTL を設定する。TTL なしのキャッシュキーはメモリリークの原因になる
  • 書き込みと同時に設定する: SET key value EX 3600(SET してから EXPIRE だとアトミックでない)
  • TTL にジッター(ランダム幅)を加える: 大量のキーが同時に失効すると、バックエンドへのアクセスが一斉に集中する(キャッシュスタンピード)
ttl = 3600 + random.randint(0, 300)
r.set(key, value, ex=ttl)
  • 上書き時の注意: SET は既存の TTL を消す。TTL を保持したい場合は SET ... KEEPTTL
  • Hash のフィールド単位で期限を設定する機能(HEXPIRE 等)は新しいバージョンで利用可能。使用中のバージョンを確認すること

5. パフォーマンス

ラウンドトリップを減らす

  • パイプライン: 複数コマンドをまとめて送信する(往復回数が劇的に減る)
  • 複数キーコマンド: MGET / MSET / HMGET
  • 1回のパイプラインは数百〜数千コマンド程度に区切る(巨大すぎるとメモリとレイテンシが悪化)
pipe = r.pipeline(transaction=False)
for uid in user_ids:
    pipe.hgetall(f"user:{uid}")
results = pipe.execute()

アトミックな処理

  • 複数ステップをアトミックにしたい場合は Lua スクリプト(EVAL / EVALSHA)または Functions を使う
  • MULTI/EXEC はコマンドをまとめて実行するがロールバックはない。楽観ロックが必要なら WATCH と組み合わせる
  • Lua スクリプトは実行中にサーバーをブロックするので、短く保つこと

計算量を意識する

  • O(N) のコマンドは N が小さいことが分かっている場合のみ使う
  • Sorted Set の範囲取得は件数を LIMIT で絞る

削除は非同期で

  • 大きなキーの削除は DEL ではなく UNLINK(バックグラウンドで解放)
  • 以下を有効化して、内部的な削除処理も非同期化する
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
lazyfree-lazy-user-del yes

6. 避けるべきコマンド・アンチパターン

NG理由代替
KEYS *全キー走査でサーバーをブロックSCAN + MATCH + COUNT
巨大コレクションへの HGETALL / SMEMBERS / LRANGE 0 -1大量データ転送とブロックHSCAN / SSCAN / ページング
本番での FLUSHALL / FLUSHDB全データ消失 + ブロックACL で禁止、必要時は ASYNC
本番での MONITOR 常用スループットが大幅に低下短時間のデバッグのみ
DEL で巨大キー削除ブロックUNLINK
Redis を唯一のデータストアとして扱う(無対策)障害時のデータ損失永続化設定 + 正のデータは RDB 等に
リクエストごとに新規接続接続コストが大きいコネクションプール
長時間実行の Lua スクリプト全体をブロック処理を分割

危険コマンドの無効化(ACL 推奨)

ACL SETUSER app on >password ~app:* +@all -@dangerous

古い手法の rename-command より ACL で権限を絞る方が管理しやすいです。


7. キャッシュ戦略

主なパターン

パターン概要向いているケース
Cache-Aside(Lazy Loading)アプリがキャッシュを確認し、なければ DB から読んで格納最も一般的・汎用的
Write-Through書き込み時に DB とキャッシュを同時更新読み取り一貫性を重視
Write-Behindキャッシュに書いて非同期で DB 反映書き込み性能重視(データ損失リスクあり)

Cache-Aside の例

def get_user(uid):
    key = f"user:{uid}"
    cached = r.get(key)
    if cached is not None:
        return deserialize(cached)
    user = db.fetch_user(uid)
    r.set(key, serialize(user), ex=3600 + random.randint(0, 300))
    return user

典型的な問題と対策

問題内容対策
キャッシュスタンピード人気キーの失効直後に大量リクエストが DB に殺到ロック(SET lock NX EX)で1リクエストだけ再生成 / 期限前の先行更新 / TTL ジッター
キャッシュ貫通(Penetration)存在しないキーへの問い合わせが毎回 DB に届く「存在しない」結果も短い TTL でキャッシュ / Bloom Filter
キャッシュ雪崩(Avalanche)大量キーの同時失効や Redis 停止TTL ジッター / 高可用構成 / サーキットブレーカー
不整合DB 更新後もキャッシュが古い更新時は「DB 更新 → キャッシュ削除」を基本にする

分散ロックの注意

  • SET lock:resource <一意な値> NX PX 30000 で取得
  • 解放時は「値が自分のものか確認してから削除」を Lua でアトミックに行う
  • 正確性が厳密に求められるロック(金銭処理など)は、フェンシングトークンを使うか、より強い一貫性を持つ仕組み(DB のロック等)を検討する

8. 永続化

方式特徴データ損失の可能性
なし純キャッシュ再起動で全消失
RDB定期スナップショット。復旧が速くファイルが小さい最後のスナップショット以降
AOF書き込みログ。appendfsync everysec が一般的最大約1秒
RDB + AOF両方の利点。AOF の書き換えにも RDB 形式が使われる最大約1秒

推奨

  • 純キャッシュ → 永続化なし、または RDB のみ
  • 失いたくないデータ → AOF(everysec)+ RDB
  • バックアップとして RDB ファイルを別の場所(オブジェクトストレージ等)に定期コピーする
  • 定期的にリストア手順をテストする
  • fork 時のレイテンシに注意。Linux では以下を推奨:
    • vm.overcommit_memory = 1
    • Transparent Huge Pages(THP)を無効化

9. 高可用性とスケーリング

構成の選択肢

構成用途
単一インスタンス開発・検証、失っても問題ないキャッシュ
レプリケーション + Sentinelデータ量が1ノードに収まり、自動フェイルオーバーが必要
Redis Clusterデータ量・スループットを水平分割したい
マネージドサービス(ElastiCache / MemoryStore / Azure Cache 等)運用負荷を下げたい

ポイント

  • レプリケーションは非同期。フェイルオーバー時に直近の書き込みが失われる可能性がある
    • WAIT コマンドで同期を待てるが、強い一貫性は保証されない
  • min-replicas-to-write / min-replicas-max-lag で、レプリカと切り離されたマスターへの書き込みを抑制できる
  • 読み取りをレプリカに分散する場合、レプリケーション遅延(古いデータ)を許容できるかを確認する
  • Cluster ではマルチキー操作に制約がある(ハッシュタグで回避)
  • Sentinel は 3台以上(奇数) を別々の障害ドメインに配置する
  • Cluster 対応クライアントを使い、MOVED / ASK リダイレクトを正しく処理させる

10. セキュリティ

  • インターネットに直接公開しない。プライベートネットワーク内に置き、bind で待ち受けを限定
  • protected-mode yes を維持する
  • ACL で認証と権限を設定(デフォルトユーザーの無効化・用途別ユーザー・キーパターン制限)
ACL SETUSER default off
ACL SETUSER app on >strong-password ~app:* &app:* +@read +@write -@dangerous
ACL SETUSER monitor on >another-password +info +ping +client|list
  • TLS を有効化(特にネットワークを跨ぐ場合)
  • パスワードはコードに直書きせず、シークレット管理(Vault / Secrets Manager 等)を使う
  • 不要・危険なコマンド(FLUSHALL, CONFIG, DEBUG, KEYS 等)をアプリ用ユーザーから剥奪
  • 定期的にバージョンアップし、セキュリティ修正を適用する
  • Lua やモジュールに関する脆弱性情報もウォッチする

11. クライアント・接続管理

  • コネクションプールを使う(リクエストごとの接続確立は避ける)
  • タイムアウトを必ず設定する
    • 接続タイムアウト・コマンドタイムアウトを短めに(例: 数百ms〜数秒)
  • リトライは指数バックオフ + ジッターで。即時の無限リトライは障害を悪化させる
  • Redis 障害時にアプリ全体が止まらないよう、キャッシュ障害時は DB にフォールバックする設計にする(サーキットブレーカー)
  • maxclients と OS のファイルディスクリプタ上限(ulimit -n)を確認する
  • timeout(アイドル接続の切断)や tcp-keepalive を適切に設定する
  • CLIENT SETNAME で接続に名前を付けると、CLIENT LIST での調査が楽になる
  • 読み取りが非常に多いなら、クライアントサイドキャッシュ(CLIENT TRACKING)も検討

12. 監視・運用

監視すべき主要メトリクス

メトリクス確認方法注目点
メモリ使用量INFO memoryused_memory vs maxmemory、断片化率
ヒット率INFO statskeyspace_hits / (hits + misses)
退避数INFO statsevicted_keys の急増はメモリ不足のサイン
接続数INFO clientsconnected_clients、blocked_clients
レイテンシLATENCY DOCTOR / redis-cli --latencyスパイクの有無
スローコマンドSLOWLOG GET想定外の重いコマンド
レプリケーションINFO replication遅延(オフセット差)、リンク状態
永続化INFO persistencerdb_last_bgsave_status、AOF 書き換えの状態
CPUOS / INFO cpuRedis のコマンド処理は基本シングルスレッド

設定例

slowlog-log-slower-than 10000   # 10ms 以上を記録(単位: マイクロ秒)
slowlog-max-len 1024
latency-monitor-threshold 100   # 100ms 以上を記録

運用のコツ

  • Prometheus(redis_exporter)+ Grafana などでダッシュボード化し、アラートを設定する
  • メモリ使用率・退避数・レプリケーション遅延・接続数にはアラートを必ず設ける
  • 負荷試験は redis-benchmark や memtier_benchmark で、本番に近いデータサイズ・コマンド比率で行う
  • I/O スレッド(io-threads)はネットワーク処理がボトルネックの高負荷環境で効果がある。導入前にベンチマークで確認する

13. チェックリスト

設計

  • キー命名規則が文書化されている
  • ビッグキー・ホットキーが発生しない設計になっている
  • 用途に適したデータ型を選んでいる
  • キャッシュキーにはすべて TTL(+ジッター)が設定されている
  • キャッシュと永続データが分離されている(または退避ポリシーが適切)

設定

  • maxmemory と maxmemory-policy が設定されている
  • lazyfree 系の設定が有効
  • 用途に合った永続化設定(RDB / AOF)
  • vm.overcommit_memory=1、THP 無効化

セキュリティ

  • 外部に公開されていない
  • ACL で用途別ユーザー・最小権限を設定
  • TLS 有効化(必要な場合)
  • 危険コマンドがアプリから実行できない

アプリケーション

  • KEYS を使っていない(SCAN を使用)
  • パイプライン / 複数キーコマンドでラウンドトリップを削減
  • コネクションプール、タイムアウト、バックオフ付きリトライ
  • Redis 障害時のフォールバックがある

運用

  • 主要メトリクスの監視とアラートがある
  • SLOWLOG / レイテンシ監視が設定されている
  • バックアップの取得とリストアのテストを定期実施
  • 高可用構成(Sentinel / Cluster / マネージド)を採用している(必要な場合)
  • バージョンアップ計画がある

参考

0件のページ