【実務・中級編】 auto-aof-rewrite-percentage設定 – Redis

RedisのAOF爆発を防ぐ:`auto-aof-rewrite-percentage` のメカニズムと本番運用の急所

Redisを単なるインメモリキャッシュではなく、データの永続化を前提としたデータストアとして本番運用する際、多くのエンジニアが一度は直面するのが「AOF(Append Only File)の肥大化と、それに伴う予期せぬI/Oスパイク・メモリ枯渇」です。

今回は、AOFの自動圧縮プロセスを司る最重要パラメータの一つ、`auto-aof-rewrite-percentage` について、その内部挙動から実務における堅牢な設計パターンまでをロジカルに解説します。

—

1. AOF Rewriteの基本メカニズムとトリガー条件

Redisはすべての書き込み操作をAOFファイルに逐次追記します。当然、同一キーへの上書きや削除が繰り返されると、ファイル内には「最終状態の復元に不要な冗長コマンド」が蓄積します。これを解消し、現在のメモリ状態を最小限のコマンドセットで再構築する処理が AOF Rewrite(`BGREWRITEAOF`) です。

Redisが自動でAOF Rewriteを実行するか否かは、以下の2つのパラメータの積集合(AND条件)で決定されます。

1. auto-aof-rewrite-percentage (デフォルト: 100)
2. auto-aof-rewrite-min-size (デフォルト: 64mb)

判定ロジックの内部構造

Redis内部では、前回のRewrite完了時(または起動時)のファイルサイズを `aof_base_size`、現在のAOFファイルサイズを `aof_current_size` として保持しています。

自動書き換えが発火する条件式は以下の通りです。

$$\text{aof\_current\_size} \ge \text{auto-aof-rewrite-min-size}$$
$$\text{かつ}$$
$$\frac{\text{aof\_current\_size} – \text{aof\_base\_size}}{\text{aof\_base\_size}} \times 100 \ge \text{auto-aof-rewrite-percentage}$$

[前回リライト直後] aof_base_size = 1GB
│
▼ (書き込み継続)
[AOF増加中…] aof_current_size = 1.5GB (増加率 50%) -> 発火しない
│
▼
[閾値到達] aof_current_size = 2.0GB (増加率 100%) -> BGREWRITEAOF 自動トリガー!

—

2. デフォルト値「100%」に潜む本番運用の罠

開発環境ではデフォルトの `auto-aof-rewrite-percentage 100` で何の問題も起きません。しかし、データサイズが数十GBに達する本番環境では、この「100%(=前回の2倍)」という設定がクリティカルなインシデントの引き金になります。

① 「倍々ゲーム」によるディスク容量枯渇

ベースサイズが大きくなるほど、次回のリライトまでの絶対的な猶予サイズが倍増します。

  • Base: 10GB $\rightarrow$ 次回実行は 20GB 到達時(差分+10GB)
  • Base: 50GB $\rightarrow$ 次回実行は 100GB 到達時(差分+50GB)

Rewrite中には「既存のAOF」「新規作成中のAOF」「書き換え中に蓄積される差分バッファ」がディスク上に共存するため、一時的にベースサイズの2倍〜2.5倍のディスク容量を消費します。容量設計を見誤ると、ディスクフルによる書き込み停止(`MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk` 等)に直結します。

② Copy-on-Write(CoW)とメモリ消費のスパイク

`BGREWRITEAOF` は `fork()` システムコールを発行し、子プロセスで実行されます。LinuxのCopy-on-Write機構により、子プロセスの生成自体は軽量ですが、Rewrite中に親プロセスへ大量の書き込みが発生すると、メモリページの複製が急増します。

特に巨大なインスタンスで `percentage 100` に達するほどの高負荷状態下でRewriteが走ると、メモリ使用率が急上昇し、最悪の場合OSのOOM KillerによってRedisプロセスが強制終了されます。

③ Fork時のレイテンシ(Stop the World)

メモリサイズが大きいインスタンスほど、`fork()` 実行時のページテーブル複製コストが増大します。数GB〜数十GB規模のRedisでは、`fork()` の一瞬だけで数十ミリ秒〜数百ミリ秒、メインスレッドのイベントループがブロックされます。

—

3. 実務における堅牢な設計パターン

では、本番環境ではどのように設計・チューニングすべきでしょうか。推奨されるアプローチは2つあります。

パターンA: 増加率をタイトにし、早期に小さく刈り取る

トラフィックが比較的均等で、かつディスクI/Oに余裕があるシステムでは、パーセンテージを下げて「小さなRewriteを頻繁に行う」アプローチをとります。

redis.conf

最小サイズを実務的な値に引き上げる(小さすぎる頻発を防ぐ)
auto-aof-rewrite-min-size 1gb

前回到達時から30%増加した時点で早期にリライトを実行する
auto-aof-rewrite-percentage 30

  • メリット: AOFの肥大化を防ぎ、1回あたりのRewrite負荷(CoWメモリとI/O消費)を低減できる。
  • デメリット: 書き込み頻度が高い環境では、Rewrite処理自体の頻度が増えるため、常時バックグラウンドI/Oが発生する。

—

パターンB: 自動リライトを無効化し、Cronでスケジューリング(推奨)

大規模な本番環境、特にレイテンシに極めてシビアなシステムで最も信頼性が高いのは、自動リライトを無効化(または形骸化)させ、トラフィックの谷間に明示的に実行するパターンです。

redis.conf

0を指定することで自動AOF書き換えを完全に無効化する
auto-aof-rewrite-percentage 0

運用側の制御として、夜間などのオフピーク時間帯にバッチや運用スクリプトからCLI経由で `BGREWRITEAOF` を発行します。

!/bin/bash
set -euo pipefail

REDIS_CLI=”redis-cli -h 127.0.0.1 -p 6379″

1. 既にBGSAVEやAOF Rewriteが実行中ではないか確認
AOF_REWRITE_IN_PROGRESS=$($REDIS_CLI info persistence | grep -E ‘^aof_rewrite_in_progress:’ | awk -F: ‘{print $2}’ | tr -d ‘\r’)
RDB_BGSAVE_IN_PROGRESS=$($REDIS_CLI info persistence | grep -E ‘^rdb_bgsave_in_progress:’ | awk -F: ‘{print $2}’ | tr -d ‘\r’)

if [ “$AOF_REWRITE_IN_PROGRESS” = “0” ] && [ “$RDB_BGSAVE_IN_PROGRESS” = “0” ]; then
echo “Starting BGREWRITEAOF…”
$REDIS_CLI BGREWRITEAOF
else
echo “Warning: Another persistence task is running. Skipping.”
exit 1
fi

  • メリット: ピーク時の予期せぬフォークやディスクI/Oの競合を100%排除できる。
  • デメリット: スケジューラの実装・監視責務がインフラ/アプリ層に移管される。

—

4. 監視すべきメトリクス(INFO Persistence)

`auto-aof-rewrite-percentage` をチューニングする際は、以下のメトリクスをPrometheusやDatadog等で必ず可視化してください。

127.0.0.1:6379> INFO persistence
Persistence
aof_enabled:1
aof_current_size:10737418240 # 現在のAOFサイズ (約10GB)
aof_base_size:5368709120 # 前回Rewrite完了時のサイズ (約5GB)
aof_pending_rewrite:0 # BGSAVE完了待ちのRewriteフラグ
aof_buffer_length:0 # AOFバッファのバイト数
aof_rewrite_buffer_length:0 # Rewrite中に蓄積されるバッファ
latest_fork_usec:12400 # 直近のfork()にかかった時間 (マイクロ秒)

特に注目すべきは以下の2点です:
1. `latest_fork_usec`: ここが `50000`(50ms)を超えてくると、クライアント側でタイムアウトが発生し始めます。インスタンスサイズの分割(シャーディング)を検討する明確なシグナルです。
2. `aof_current_size` と `aof_base_size` の乖離: アプリケーションの書き込みパターンに対して設定したパーセンテージが適切に機能しているかを評価する指標となります。

—

まとめ:テクニカルリードとしての判断基準

  • `auto-aof-rewrite-percentage` は「ファイルサイズ制御」ではなく「システムリソースのスパイク制御」のパラメータである。
  • 数GB未満の小規模インスタンスであれば `auto-aof-rewrite-percentage 50` 〜 `100` の自動制御で十分。
  • 数十GB規模の大規模・低遅延要件では `auto-aof-rewrite-percentage 0` による明示的トリガー制御をファーストチョイスとする。
  • 不用意なフォークによるブロッキングを防ぐため、`latest_fork_usec` の監視を設計に必ず組み込む。

パラメータ一つひとつの裏にあるOSレイヤの挙動(I/O、Memory、Page Table Copy)まで見通してアーキテクチャを決定しましょう。

コメント

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