【テクニカル・上級編】 Redis永続化の概要 – Redis

Redis永続化の真実:RDBとAOFの内部メカニズムとアーキテクトの選択

Redisは、その圧倒的なスループットとミリ秒未満のレイテンシから、しばしば「単なるインメモリ・キャッシュストア」として片付けられがちだ。しかし、現代のシステムアーキテクチャにおいて、Redisは信頼性の高いプライマリ・データストア、あるいは複雑な分散システムのステート管理ハブとして機能している。

ここで問われるのが 「永続化(Persistence)」 のメカニズムだ。

揮発性メモリ(DRAM)の極限パフォーマンスを維持しながら、如何にしてデータの耐久性(Durability)を担保するか。Redisが提供する二つのアプローチ、すなわち RDB(Redis Database) と AOF(Append Only File) は、それぞれ異なるエンジニアリング上のトレードオフの上に成り立っている。

本稿では、教科書的な機能説明を一切排し、OSカーネルの挙動、メモリ管理、そしてI/Oパイプラインの深層に踏み込み、Redis永続化の全貌を解き明かす。

—

1. 永続化のパラドックス:メモリとディスクの不可避な摩擦

Redisの全データはメモリ上に存在する。CPUキャッシュの局所性を最大限に活かし、ノンブロッキングなイベントループで処理されるため、シングルスレッドでありながら驚異的な性能を発揮する。

しかし、メモリは揮発性である。電源が落ちればデータは消える。
ここにデータベースとしての永遠のジレンマがある。「高速なインメモリ処理」と「非揮発性ストレージへの書き込み」は、物理的特性において完全に背反するのだ。

Redisはこの摩擦を解決するために、以下の二つの異なる数学的・構造的アプローチを採用した。

1. RDB (Point-in-time Snapshotting): 特定時点のメモリ上のスプレッドをバイナリとしてダンプする。
2. AOF (Append Only Log): すべての書き込みコマンドを順次追記し、WAL(Write-Ahead Log)として保持する。

これらは単なる設定オプションの選択ではない。システムの障害耐性(RPO / RTO)、ディスクI/Oの特性、そしてレイテンシのスパイクを決定づけるアーキテクチャの根幹である。

—

2. RDB: ゼロ・コピーの幻想とOSフォークの魔術

RDBは、指定された間隔(例: `900秒以内に1キー以上変更`)でメモリのスナップショットをディスクに書き出す方式だ。

`fork()` と Copy-on-Write (CoW) の実態

Redisはシングルスレッドでリクエストを処理している。数数十GBに及ぶメモリ空間を、メインスレッドがそのままディスクにシリアライズしていたら、I/O待ちの間、すべてのクライアントリクエストがブロック(フリーズ)してしまう。

これを回避するためにRedisが使うのが、UNIXの基本プリミティブである `fork()` だ。

/ Redis内部でのRDB保存トリガーの概念コード /
pid_t pid = fork();
if (pid == 0) {
// 子プロセス: メモリのスナップショットをディスクに吐き出す
rdbSave();
exit(0);
} else if (pid > 0) {
// 親プロセス: 通常通りクライアントからのリクエストを処理し続ける
}

ここでLinuxカーネルの Copy-on-Write (CoW) メカニズムが機能する。`fork()` 直後、子プロセスは親プロセスと全く同じ物理メモリのページテーブル(仮想アドレスから物理アドレスへのマッピング)を共有する。メモリの物理コピーは行われないため、`fork()` 自体は一瞬で終わるように見える。

アーキテクトが知るべき「CoWの隠れたコスト」

しかし、ここに大きな罠がある。親プロセス(メインスレッド)が既存のキーを更新(UPDATE)したり削除(DELETE)したりすると、OSは該当するメモリページを別領域にコピーし、書き込みを行う。

  • メモリ使用量のスパイク: 更新頻度が高い(Write-heavyな)ワークロードでは、RDB保存中にメモリ消費量が理論上の最大で2倍に膨れ上がる。物理メモリが枯渇すれば、LinuxのOOM KillerによってRedisプロセスが強制終了されるか、スワップアウトが発生してレイテンシが崩壊する。
  • 巨大なページテーブルの走査: 巨大なデータセット(数十GB〜数百GB)を扱う場合、`fork()` 自体の実行時に親プロセスのページテーブルを複製するため、数ミリ秒から数十ミリ秒単位のマイクロ・ストップ(レイテンシのスパイク)が発生する。これはミリ秒を争うHFT(高頻度取引)などのシステムでは致命傷になり得る。

—

3. AOF: WALの極意と再構築の限界

RDBの最大の弱点は「スナップショット間のデータ損失(RPOの拡大)」である。これを補うのが AOF (Append Only File) だ。

AOFは、Redisが受けるすべての書き込みコマンドを、Redis自身のプロトコルフォーマットでファイル末尾にひたすら追記していく。

fsync ポリシーの選択と耐久性のトレードオフ

AOFの性能とデータロストの許容範囲は、OSのファイルシステムキャッシュとどう同期(`fsync`)させるかに完全に依存する。設定ファイルにある `appendfsync` の3つのオプションを低レイヤから見直す。

| 設定値 | 挙動 | RPO (データ損失リスク) | スループット / レイテンシ |
| :— | :— | :— | :— |
| `always` | コマンド毎に `fsync` を明示的に発行 | ほぼ 0 (最大1コマンド) | 最低 (ディスクI/Oがボトルネック) |
| `everysec` | バックグラウンドスレッドで1秒に1回 `fsync` | 最大1秒分 | 高い (実用的なバランス) |
| `no` | OSのカーネルがよしなにフラッシュするに任せる | OS依存 (数秒〜数十分) | 最大 (通常のファイル書き込みと同等) |

実運用において `always` を選択することは、SSDの寿命を削るだけでなく、ストレージのI/O待ちでRedisのシングルスレッドモデルを完全に窒息させるため、ほぼタブーとされる。
`everysec` が黄金律であり、万が一の障害時にも失われるデータを最大1秒に抑えつつ、ディスクの帯域を効率的に利用できる。

AOF Rewrite (BGREWRITEAOF) の内部構造

AOFを使い続けると、ファイルサイズは無限に肥大化する。
例えば、`SET counter 1` を100回繰り返した場合、AOFには100行のログが残るが、最終的な状態として必要なのは `SET counter 100` の1行だけだ。

Redisはこの肥大化を防ぐために AOF Rewrite を行う。
親プロセスが `BGREWRITEAOF` を叩くと、子プロセスが `fork()` され、現在のメモリ上のデータを逆算して、最小限のコマンド群として新しいAOFファイルをスクラッチから構築する。

ここで賢いのは、rewrite実行中に親プロセスに届いた新たな書き込みコマンドは、古いAOFファイルに追記されつつ、「AOF rewrite buffer」にも並行してバッファリングされる点だ。新しいAOFファイルの生成が完了すると、このバッファ分のコマンドがアトミックに追記され、ファイルが切り替えられる。

—

4. アーキテクトのための実践的設計指針:RDBか、AOFか、ハイブリッドか?

では、実際のプロダクション環境において、我々はどのような永続化戦略を採るべきなのか。

1. キャッシュ層としての割り切り(Persistence Off)

データが完全にロストしても、下流のDB(RDB/NoSQL)から再構築可能な純粋なキャッシュであれば、RDBもAOFも完全に無効化(`save “”`, `appendonly no`)するのが正解である。
ディスクI/Oのオーバヘッド、CoWによるメモリ枯渇リスク、`fork()` によるレイテンシスパイクから解放され、Redisの真のパフォーマンスを引き出せる。

2. 近代Redisのスタンダード:RDB + AOF ハイブリッド

Redis 4.0以降、「RDBプレフィックス付きAOF(RDB-AOF Hybrid Persistence)」という強力なオプションが導入された。

  • 設定: `aof-use-rdb-preamble yes`

このモードでは、AOFのファイル前半にRDBのスナップショットを置き、後半にそのスナップショット以降のAOF差分コマンドを配置する。
これにより、以下のメリットを同時に享受できる。

  • 高速なリカバリ: 再起動時に巨大なAOFログを最初から1行ずつ再生(replay)する必要がなくなり、RDB部分を高速にメモリへロードした後、わずかなAOF差分だけを適用すればよいため、復旧時間が劇的に短縮される。
  • 堅牢な耐久性: AOFの構造を維持しているため、データ損失のリスクを最小限に抑えられる。

—

5. 結言:ハードウェアとOSをハックする視点

Redisの永続化メカニズムは、単なる設定値のチューニングではない。
Linuxカーネルのメモリ管理(Virtual Memory)、ファイルシステムのジャーナリング、そしてストレージ(NVMe SSD等)のIOPS性能という、インフラストラクチャの物理的限界との絶妙なダンスである。

アーキテクトに求められるのは、ワークロードの性質(Read/Write比率、データの重要度、許容できるRPO/RTO)を正確にプロファイリングし、適切なトレードオフを選択することだ。

Redisを使いこなすとは、すなわちOSとハードウェアの挙動を掌の上で操ることと同義なのだから。

コメント

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