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

Redis永続化の落とし穴:なぜデフォルトの `auto-aof-rewrite-min-size` を本番環境で放置してはならないのか

設計レビューの現場で、Redisの設定ファイル(`redis.conf`)にデフォルト値がそのまま残されているケースを頻繁に見かけます。特にAOF(Append Only File)の自動書き換えパラメータである `auto-aof-rewrite-min-size` は、多くのエンジニアが「とりあえずデフォルトの64MBで動いているから問題ない」と見過ごしがちな設定の筆頭です。

断言しますが、本番の高トラフィック環境において、デフォルトの64MB設定はパフォーマンス障害の火種にしかなりません。

本稿では、このパラメータの内部挙動をカーネルレベルの挙動まで掘り下げ、なぜこの値の適切なチューニングが不可欠なのか、そして現場で採用すべき堅牢な設計パターンを解説します。

—

1. `auto-aof-rewrite-min-size` の本質とトリガー条件

RedisのAOFは、すべての書き込みコマンドを追記(Append)していくため、放置すればディスクを食いつぶし、リカバリ時間(RTO)を極度に悪化させます。これを防ぐために実行されるのが、メモリ上の最新状態をスナップショットとして再構築する AOF Rewrite(`BGREWRITEAOF`) です。

自動でRewriteがトリガーされる条件は、Redis内部(`serverCron`)で以下のように厳密に評価されています。

AOF書き換えトリガー条件:
(current_size > auto-aof-rewrite-min-size)
&&
( (current-size – base_size) / base_size 100 >= auto-aof-rewrite-percentage )

  • `auto-aof-rewrite-percentage`: 前回のRewrite完了時(または起動時)のファイルサイズ(`base_size`)から、何%肥大化したら実行するか(デフォルト: `100`% = 2倍)。
  • `auto-aof-rewrite-min-size`: 「どんなにパーセンテージ条件を満たしていても、このサイズを超えるまではRewriteを発火させない」 という絶対的な下限値(デフォルト: `64mb`)。

なぜ「最小サイズ」が必要なのか?

もしこの設定値が存在しない、あるいは極端に小さい場合を考えてみてください。
起動直後に `base_size` が1MBだった場合、わずか2MBに達した時点でRewriteが発火します。次は4MB、8MB、16MB……と、データ量が微小な立ち上がりフェーズで短時間に連続してRewrite(fork)が頻発する「Fork Storm」に陥ります。

これを防ぐための安全弁が `auto-aof-rewrite-min-size` です。しかし、問題はその「64MB」という値が、現代の大規模インフラのスケールに対してあまりに小さすぎる点にあります。

—

2. デフォルト値が引き起こす本番障害のメカニズム

なぜ64MBでは危険なのか。AOF Rewriteが実行される際のRedisの内部挙動とリソース消費を紐解きます。

① `fork()` システムコールによるイベントループのブロック

AOF Rewriteはバックグラウンドプロセス(子プロセス)で行われますが、子プロセスを生成する `fork()` 自体はメインスレッドをブロックします。
メモリ使用量が数十GB規模のインスタンスでは、ページテーブルのコピーだけでも `fork()` に数十〜数百ミリ秒を要し、その間Redisは一切のクライアントリクエストを処理できません(レイテンシスパイクの発生)。

② Copy-on-Write (CoW) によるメモリの急激な枯渇

子プロセス生成後、親プロセス(メインスレッド)に書き込みが発生すると、対象のメモリページが複製(CoW)されます。高スループット環境下でRewriteが走ると、メモリ消費量が瞬時に跳ね上がり、最悪の場合 OOM KillerによってRedisプロセスが強制終了 します。

③ 高頻度な低容量書き換えによるディスクI/Oの飽和

実データサイズが小さい(例: 20MB)にもかかわらず、毎秒数万件の更新・削除(churn)が発生するキー設計の場合、AOFログだけが猛烈な勢いで肥大化します。
`min-size` が64MBだと、数分おきに64MB到達→Rewrite→20MBに縮小→再び64MB到達……という無限ループに陥り、ディスクI/O帯域を無駄に浪費し続けます。

—

3. 実務で採用すべき設計パターンと設定例

パターンA:インメモリサイズに応じた動的サイジング(標準パターン)

原則として、`auto-aof-rewrite-min-size` は 「想定される定常状態のメモリ使用量(データセット実サイズ)」よりも明確に大きな値 に設定します。

推奨設計基準:
auto-aof-rewrite-min-size = Max( 実データセットの想定容量 × 1.5 〜 2.0, 1GB〜数GB )

設定例 (`redis.conf`)

メモリ割り当て(`maxmemory`)が16GB、実データが常時4〜8GB程度存在するシステムの場合:

==========================================
AOF Persistence Production Configuration
==========================================

AOFを有効化
appendonly yes

ディスク同期戦略(パフォーマンスと耐久性のバランス)
appendfsync everysec

前回サイズから100%増加(2倍)でトリガー
auto-aof-rewrite-percentage 100

【重要】デフォルトの64mbから引き上げ。
8GBのデータセットが定常である場合、最低でも8GB〜16GB程度まで引き上げる。
これにより、システム起動直後や低負荷時の無駄なfork頻発を抑止する。
auto-aof-rewrite-min-size 8gb

AOF Rewrite実行中、メインプロセスのfsync呼び出しを抑制してディスクI/O競合を回避
no-appendfsync-on-rewrite yes

—

パターンB:大規模インスタンスにおける「自動書き換えの完全無効化と外部スケジューリング」

データセットが32GB〜64GBを超えるような超大規模Redisクラスタでは、日中のピークトラフィック時に自動Rewriteがトリガーされること自体がSLA違反のリスクとなります。
この場合、自動書き換えを意図的に無効化し、トラフィックの谷間に外部から制御する パターンがエンタープライズアーキテクチャの定石です。

1. 設定ファイルで自動Rewriteを無効化

percentageを0に設定することで、自動トリガーを完全に無効化
auto-aof-rewrite-percentage 0

安全のためmin-sizeも現実的な上限にしておく
auto-aof-rewrite-min-size 64gb

2. 定期ジョブ(Cron / Kubernetes CronJob)で制御実行

トラフィックの最も低い時間帯(例: 深夜4時)に、スクリプトから安全に `BGREWRITEAOF` を発行します。

!/usr/bin/env bash
set -euo pipefail

REDIS_HOST=”127.0.0.1″
REDIS_PORT=”6379″

1. すでにBGSAVEやAOF Rewriteが実行中ではないか確認
AOF_REWRITE_IN_PROGRESS=$(redis-cli -h “$REDIS_HOST” -p “$REDIS_PORT” INFO persistence | grep -E ‘^aof_rewrite_in_progress:’ | awk -F: ‘{print $2}’ | tr -d ‘\r’)
BGSAVE_IN_PROGRESS=$(redis-cli -h “$REDIS_HOST” -p “$REDIS_PORT” INFO persistence | grep -E ‘^rdb_bgsave_in_progress:’ | awk -F: ‘{print $2}’ | tr -d ‘\r’)

if [ “$AOF_REWRITE_IN_PROGRESS” = “1” ] || [ “$BGSAVE_IN_PROGRESS” = “1” ]; then
echo “[WARN] Another background save/rewrite is already in progress. Skipping.”
exit 0
fi

2. Rewriteコマンドの明示的発行
echo “[INFO] Triggering BGREWRITEAOF…”
redis-cli -h “$REDIS_HOST” -p “$REDIS_PORT” BGREWRITEAOF

3. 完了待機(ポーリング)
while true; do
STATUS=$(redis-cli -h “$REDIS_HOST” -p “$REDIS_PORT” INFO persistence | grep -E ‘^aof_rewrite_in_progress:’ | awk -F: ‘{print $2}’ | tr -d ‘\r’)
if [ “$STATUS” = “0” ]; then
echo “[INFO] BGREWRITEAOF completed successfully.”
break
fi
echo “[INFO] Waiting for rewrite to finish…”
sleep 5
done

—

4. 運用時に監視すべきメトリクス(`INFO persistence`)

設計が正しく機能しているかを判断するために、以下のメトリクスを必ずDatadogやPrometheusで監視してください。

127.0.0.1:6379> INFO persistence
Persistence
aof_enabled:1
aof_current_size:9126805432 # 現在のAOFファイルサイズ(バイト)
aof_base_size:4563402716 # 前回Rewrite完了時のサイズ
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0
aof_last_rewrite_time_sec:42 # 前回のRewrite所要時間(秒)
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:419430400 # 前回のRewriteで発生したCoWのメモリ量(バイト)
latest_fork_usec:18450 # ★直近のfork()にかかった時間(マイクロ秒)

チーフアーキテクトの監視チェックポイント

1. `latest_fork_usec` が 50,000(50ミリ秒)を超えているか?

  • 超過している場合、メインスレッドのブロッキングがアプリ層でタイムアウトを引き起こしている可能性があります。`auto-aof-rewrite-min-size` を引き上げて実行頻度を下げるか、ホストのメモリ管理(HugePagesの無効化など)を見直してください。

2. `aof_last_cow_size` の割合

  • メモリ許容量に対してCoWの増加量が大きすぎる場合、Rewrite中の書き込みトラフィックが高すぎます。実行タイミングを夜間にズラすか、レプリカノード側でのみ永続化を行うアーキテクチャへの移行を検討すべきです。

—

まとめ

`auto-aof-rewrite-min-size` の設定は、単なる「容量の閾値決め」ではありません。「いつ、どの程度の頻度でメインスレッドをブロック(fork)させ、ディスク帯域を子プロセスに明け渡すか」という、システム全体のキャパシティ設計そのものです。

1. デフォルトの `64mb` は今すぐ見直す。
2. 実データサイズに基づき、ギガバイト単位で設定値を引き上げる。
3. 大規模システムでは自動書き換えに依存せず、スケジュール制御(手動トリガー)を検討する。

自身の担当するRedisクラスタの設定値がどうなっているか、今すぐレビューを実施してください。

コメント

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