RedisのAOF(Append Only File)を極限まで使い倒す:障害ゼロ・レイテンシ最小化を両立するアーキテクチャ設計
こんにちは。テックリードの私だ。
今日のコードレビュー、あるいはシステム設計レビューで、こんな議論が出たとしよう。
「データの永続化?とりあえずRDBと一緒で、デフォルトのままでいいっしょ。データ飛んだらその時考えようぜ」
もし君のチームでこんな空気が流れているなら、今すぐその手を止めさせたまえ。
Redisは「ただの速いキャッシュ」ではない。現代のマイクロサービスアーキテクチャにおいて、低レイテンシなインメモリデータベース、あるいはプライマリのデータストアとして、システム全体の生死を握る心臓部なのだから。
特に、データ損失(Data Loss)を許容できないシステムにおいて、AOF(Append Only File)の仕組みと特性を正しく理解し、チューニングできているかどうかは、シニアエンジニアとそうでないエンジニアを分かつ決定的な境界線となる。
今回は、RedisのAOFメカニズムの深層に潜り込み、実務で絶対に踏み抜いてはいけない地雷と、極限までパフォーマンスと堅牢性を両立させる設計パターンを伝授しよう。
—
1. AOFの核心:なぜ「追記」が最強の選択肢なのか
RDB(Snapshot / RDB方式)が「ある瞬間のメモリのスナップショットをバイナリでドンと書き出す」アプローチであるのに対し、AOFは「クライアントから受け取った書き込みコマンドを、Redisプロトコル(RESP)のテキスト形式でファイルにひたすら追記していく」アプローチだ。
[Client] —> WRITE Command (“SET user:101 Alice”) —> [Redis Server]
│
In-Memory Data Modified
│
Append to AOF Buffer
│
▼
[appendonly.aof]
AOFの圧倒的なメリット
1. 圧倒的な耐久性(Durability): 設定次第では、書き込みごとのディスク同期が可能。
2. 人間が読めるログ: RESPフォーマットのテキストなので、最悪の場合、ファイルをエディタで開いて破損箇所を修復したり、コマンドを解析できる。
3. 安全なリストア: サーバー再起動時は、このAOFファイルを頭から順に再実行(Replay)することでメモリを完全に復元する。
しかし、ここでエンジニアとしての嗅覚が働くはずだ。「すべてのコマンドを追記し続けたら、ファイルサイズが無限に膨れ上がるのでは?」と。
その通り。だからこそRedisにはAOFリライト(Rewrite)という洗練されたバックグラウンド処理が存在する。
—
2. 実務で知るべき「AOFリライト」の裏側
ファイルサイズ肥大化と復元時間の増大を防ぐため、Redisは現在のメモリ上のデータを復元できる「最小限のコマンド群」にAOFファイルを縮小・再構築する機能を持っている。これがAOFリライトだ。
リライトのメカニズム(Forkの魔法)
Redisはリライトを実行する際、内部で以下のような極めてエレガントなプロセスを踏む。
1. `BGREWRITEAOF` コマンドが呼ばれると、Redisは `fork()` を実行し、子プロセスを生成する。
2. 子プロセスは、現在のメモリのスナップショットをベースに、新しいAOFファイルの生成を開始する。
3. 親プロセスは、引き続きクライアントからの新しい書き込みを受け付け続ける。この間、親プロセスは新旧両方のAOFバッファに書き込みを行う(データの整合性を担保するため)。
4. 子プロセスが新しいAOFファイルの生成を完了すると、親プロセスにシグナルを送り、一時的に書き込みをブロックして、差分バッファを新しいファイルにフラッシュする。
5. 最後に、古いAOFファイルと新しいものをアトミックに置き換える。
ここで注意すべきは、データ量が膨大な(数テンバイト〜数十GB)Redisインスタンスにおいて `fork()` を行うと、Copy-on-Write(CoW)によるメモリの二重化とページテーブルのコピーコストにより、一時的なレイテンシスパイク(数秒のフリーズ)が発生するという点だ。
大規模データを持つRedisでAOFリライトを設計する際は、OSのメモリオーバーコミット設定(`vm.overcommit_memory = 1`)や、インスタンスの分割(シャーディング)をセットで考慮しなければならない。
—
3. `appendfsync` の選択:速度と安全性のトレードオフ
AOFの挙動をコントロールする上で、最も重要な設定項目が `appendfsync` だ。ここを適当に決めているエンジニアは、コードレビューで容赦なく差し戻そう。
設定値は以下の3つ。
| 設定値 | 同期タイミング | データ損失リスク | パフォーマンス |
| :— | :— | :— | :— |
| `always` | コマンド毎に `fsync` を実行 | ほぼゼロ(直前の1コマンド分のみ) | 極めて低い(I/Oネックになる) |
| `everysec` | 1秒ごとにバックグラウンドで `fsync` | 最大1秒分のデータ損失 | 高い(実務のデファクトスタンダード) |
| `no` | OSのカーネルに依存(お任せ) | OSクラッシュ時に数秒〜数十分分の損失 | 最高(ただし非推奨) |
💡 テックリードからの設計指針
- 金融・決済・セッション管理など、1バイトのロストも許されないシステム:
`always` を検討したくなるが、SSDのI/O性能がボトルネックになり、Redisの最大の武器である「超低レイテンシ」が死ぬ。基本的には `everysec` を採用し、どうしてもという場合は複数台のレプリケーション(Master-Replica)で堅牢性を担保すべきだ。
- 一般的なWebアプリケーション、キャッシュ、ランキング、アクティビティログ:
`everysec` が唯一にして最強の選択肢だ。1秒間に数万件の書き込みをさばきつつ、万が一の電源断でも最大1秒のロストに抑えられる。
—
4. 実践:プロダクションレベルの `redis.conf` 設計
それでは、実際に実務のプロダクション環境で耐えうる、堅牢なAOF設定のサンプルを提示しよう。
==========================================
Redis AOF Configuration (Production Grade)
==========================================
AOF機能の有効化
appendonly yes
AOFファイル名
appendfilename “appendonly.aof”
ディスクへの同期方針(1秒おきのフラッシュでパフォーマンスと堅牢性を両立)
appendfsync everysec
AOFリライト実行中の fsync ブロックを防ぐ
yesの場合、AOFリライト(重いI/O)が走っている間、fsyncを遅延させる。
安全性よりレイテンシの安定性を重視する場合は yes にする。
no-appendfsync-on-rewrite yes
AOF自動リライトのトリガー条件
前回のAOFファイルサイズから100%(2倍)にサイズが増加し、かつサイズが64MB以上の場合にリライト発動
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
起動時に末尾が切れた(破損した)AOFファイルをどう扱うか
yes: 読み込み可能なところまでデータを復元し、警告ログを出して起動する(データ救命優先)
no: 起動エラーで停止する(厳密な整合性優先)
aof-load-truncated yes
RDBとAOFのハイブリッド永続化(Redis 4.0以降の強力な機能)
AOFリライト時に、ベース部分をRDBフォーマット、差分をAOFフォーマットとして保存する。
これにより、再起動時のロード時間が劇的に短縮される。
aof-use-rdb-preamble yes
特筆すべき強力な機能:`aof-use-rdb-preamble`
Redis 4.0から導入されたこの機能は、AOFリライトの際に「前半をコンパクトなRDB形式、後半を最新のAOF差分」というハイブリッド構造にするものだ。
これにより、「RDBの高速なロード性能」と「AOFのデータ安全性」の良いとこ取りができる。現代のシステム設計では、これを有効にしない理由はない。
—
5. トラブルシューティング:AOFファイル破損時の緊急対応
夜中、PagerDutyが鳴り響く。Redisサーバーが再起動に失敗している。ログを見るとこうある:
` Fatal error loading the DB: The AOF file is not valid, use check-aof to fix it.`
焦る必要はない。シニアエンジニアならこう動け。
1. 現状のバックアップ(命綱)
まず壊れたファイルを退避させる。絶対にいきなり上書きしてはならない
cp /var/lib/redis/appendonly.aof /var/lib/redis/appendonly.aof.bak
2. Redis公式の修復ツールを使う
Redisには、AOFファイルの構文エラーを検知し、壊れた末尾部分を切り捨てる(あるいは修復する)ツールが同梱されている。
AOFファイルの整合性をチェック
redis-check-aof –fix /var/lib/redis/appendonly.aof
コマンドを実行すると、以下のようなプロンプトが出る。
The file is not in a valid format.
At offset 1049202, read invalid byte: 0x58
This is the end of the file. Do you want to truncate the AOF? [yes/no]
ここで `yes` を入力すれば、破損した末尾のコマンド以降が切り落とされ、正常なファイルとして修復される。その後、Redisを再起動すればサービス復旧だ。
—
まとめ
今回の講義を総括しよう。
1. AOFは「追記ログ」: 堅牢で人間にも優しく、正確な復元が可能。
2. `appendfsync everysec` が黄金律: パフォーマンスとデータロストリスクのベストバランス。
3. リライトと `fork()` のコストを理解せよ: 大規模データではメモリとレイテンシの挙動に注意する。
4. ハイブリッド(`aof-use-rdb-preamble`)を使え: 現代のRedis運用において必須の最適化。
インフラやデータベースの挙動を「おまじない」として扱うエンジニアは、いつか必ず本番障害で痛い目を見る。
RedisのAOFの仕組みを完全に掌握した君なら、もうどんなプレッシャーのかかる設計レビューでも、自信を持ってロジカルに最適解を提示できるはずだ。
実装に妥協するな。アーキテクチャで勝て。
コメント