【実務・中級編】 永続化戦略の選択基準 – Redis

Redis永続化の真実:チーフアーキテクトが教える、妥協なきストレージ設計の極意

システム設計のレビュー会で、こんなセリフを耳にしたことはないか?

> 「とりあえずRedisの永続化は、RDBとAOFの両方(`appendfsync everysec`)を有効にしておけば安全ですよね」

この瞬間、私はコードレビューの手を止め、エンジニアの目を見る。そして静かに問いかける。
「その設計、CPUとストレージのI/Oコスト、そして万が一の障害時のRTO(目標復旧時間)を本当に計算したのか?」と。

Redisは、単なる「速いインメモリキャッシュ」ではない。使い方を誤ればシステム全体のボトルネックになり、正しく使いこなせば最強のデータストアへと変貌する。今回は、Redisの永続化メカニズム(RDBとAOF)の本質を解き明かし、ユースケースに応じた「絶対に外さない」選定基準をロジカルに伝授しよう。

—

1. 永続化エンジンの裏側:RDBとAOFの「物理」を理解する

まず、Redisがデータをディスクに書き出す仕組みの根本を押さえる。ここを理解していないと、本番環境で痛い目をみる。

RDB (Redis Database)

  • 仕組み: 指定した間隔(例: 60秒以内に1万回以上の更新)でメモリ上のデータセットのスナップショットをバイナリファイル(`dump.rdb`)として書き出す。
  • 裏側の挙動: 親プロセスから `fork()` を呼び出し、子プロセスがディスクへの書き込みを行う(Copy-on-Write)。

AOF (Append Only File)

  • 仕組み: Redisが実行したすべての書き込みコマンドを、独自のプロトコル形式でログファイルに逐次追記していく。
  • 裏側の挙動: OSのページキャッシュに書き出され、設定に応じて(毎秒、あるいはコマンド毎に)`fsync` システムコールで物理ディスクにフラッシュされる。

—

2. ユースケース別・永続化戦略の選択基準

「キャッシュ」か「プライマリデータストア」か。システムにおけるRedisの役割によって、選択すべき永続化戦略は180度変わる。

パターンA:純粋なキャッシュ層(セッションストア、APIレスポンスキャッシュ)

  • 要件: データの消失は許容できる(最悪、下流のDBから再構築可能)、あるいはセッション切れとして扱える。パフォーマンスが最優先。
  • 推奨設定: 完全無効化(Persistence Disabled)

redis.conf
スナップショット設定をすべてコメントアウト、または無効化
save “”
appendonly no

チーフアーキテクトの視点:
「キャッシュだから消えてもいい」という理由だけでRDBを回し続けるチームがあるが、これは悪手だ。データ量が増えた際、`fork()` によるメモリの二重化(Copy-on-Write)でメモリが枯渇し、OOM KillerにRedisが屠られる事故が後を絶たない。キャッシュであれば、永続化など不要。潔く捨てるべきだ。

—

パターンB:高速なインメモリDB(マスターデータ、ユーザー設定、リアルタイムカウンター)

  • 要件: サーバーの再起動や予期せぬクラッシュ時に、データ損失を最小限に抑えたいが、AOFの毎秒fsyncほどのオーバーヘッドは避けたい。
  • 推奨設定: RDB中心の運用(適度なスナップショット)

redis.conf
900秒(15分)以内に1回以上の変更
save 900 1
300秒(5分)以内に10回以上の変更
save 300 10
60秒以内に10000回以上の変更
save 60 10000

appendonly no
rdbcompression yes
rdbchecksum yes

チーフアーキテクトの視点:
データが消えても「数分前の状態」に戻ればビジネス上許容できるケースの大半は、これで十分だ。`fork()` の頻度を適切にコントロールし、CPUとI/Oのバランスを取る。

—

パターンC:ミッションクリティカルなプライマリデータストア(Pub/Subの永続化、ジョブキュー、重要メタデータ)

  • 要件: データのロストが許されない。サーバーが爆発しても、直前までの書き込みが復元されなければならない。
  • 推奨設定: AOF(`appendfsync everysec`)の単体利用

redis.conf
save “” # RDBはCPU負荷とfork時のメモリ枯渇のリスクがあるため無効化(または極端に長めの間隔に)

appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

ここで重要なのは、RDBとAOFを同時に有効にしないことだ。多くのエンジニアが「念のため両方」と言って両方有効にするが、これは最悪のアンチパターンである。バックアップ時にRDBの `fork()` とAOFの重い処理が同時に走り、レイテンシが跳ね上がる。AOFを毎秒(`everysec`)で回していれば、RDBは不要、あるいはAOFリライト時のバックアップ用として割り切るべきだ。

—

3. 堅牢な設計パターン:レプリケーションとの組み合わせ

単一ノードの永続化に頼るな。本番環境における真の堅牢性は、「レプリケーション」と「永続化」の融合によって初めて達成される。

[Primary Node (AOF: everysec)]
│
├─ (Async Replication) ──> [Replica Node A (RDB only)]
│
└─ (Async Replication) ──> [Replica Node B (Persistence Off: Read-Only/Failover Candidate)]

チーフアーキテクトからの設計レビュー指摘事項:

1. 書き込みを受け持つプライマリ(Master)では、AOF(`appendfsync everysec`)を有効にし、データの耐久性を担保する。
2. 読み取り専用のレプリカ(Replica)では、あえて永続化を無効化するか、RDBのみにする。これにより、レプリカ側でのディスクI/Oボトルネックを排除し、参照性能を極限まで高める。
3. フェイルオーバー(Redis Sentinel / Redis Cluster)を前提とする場合、マスターがクラッシュした際に古いデータを持つレプリカが昇格してデータを巻き戻す(スプリットブレイン的なデータロスト)を防ぐため、レプリカの選定基準と `min-replicas-to-write` などの設定を必ず吟味すること。

—

4. パフォーマンス上の罠と回避策

最後に、実務で必ず直面する「永続化に起因するレイテンシ悪化」への対策を授ける。

  • 罠1: `fork()` によるレイテンシのスパイク(Latency Spike)
  • 原因: メモリが数十GBに達したRedisでRDBのセーブやAOFのリライトが発生すると、`fork()` 自体に数秒かかることがあり、その間Redisが完全にフリーズする(シングルスレッドモデルの悲劇)。
  • 対策: Linuxのカーネルパラメータ `vm.overcommit_memory = 1` を必ず設定し、物理メモリの空きが足りない状態での `fork()` 失敗を防ぐ。また、物理メモリの半分以上をRedisに割り当てない(Copy-on-Writeの余白を残す)。
  • 罠2: `fsync` によるブロッキング
  • 原因: `appendfsync always` に設定している場合、すべての書き込みごとにディスクの同期書き込みを待つため、SSDの性能がそのままRedisの性能上限になる。
  • 対策: 特殊な金融系トランザクションでもない限り、`always` は使わない。実務上のデフォルトは `everysec` 一択である。

—

結びにかえて

Redisの永続化設計は、「速度(Performance)」と「耐久性(Durability)」のトレードオフの最適化に他ならない。

「なんとなく安全そうだから」という思考停止の設計は、高負荷時に必ずシステムを崩壊させる。自社のシステムが求めるデータの一貫性レベル(RPO/RTO)を冷徹に見極め、コードと設定ファイルにその意図を深く刻み込んでほしい。

あなたの書いたその設定ファイルが、深夜の障害アラートからチームを救う盾となることを期待している。

コメント

タイトルとURLをコピーしました