エラーの内容
手動でスナップショットを実行すると、Redisから以下のエラーが返ってきます:
127.0.0.1:6379> BGSAVE
(error) ERR Background save already in progress
BGSAVEのフォークがすでに実行中です — Redisは2つのフォークを同時に実行させません。ほとんどの場合、これは無害です。しかし深夜2時にデプロイを待っている状況では、何が起きているのか、いつ完了するのかを把握しておく必要があります。
なぜこのエラーが発生するのか
RedisはRDBスナップショットを処理する際に子プロセスをフォークします。フォークは同時に1つしか実行できません。このエラーは以下の場合に発生します:
- 前回の
BGSAVE— 手動またはsave設定ルールによってトリガーされたもの — がまだ完了していない BGREWRITEAOFが実行中 — 古いRedisバージョンでは、AOFの書き換えもBGSAVEをブロックする- レプリカが接続し、RedisがRDB送信のためにフォークした(レプリケーションによる暗黙的なセーブ)
何も壊れていません。Redisは二重フォークからあなたを守っています。すべきことは、現在のセーブが完了するまで待つか、そもそもセーブに時間がかかりすぎている原因を突き止めることです。
ステップ1 — 実際に何が実行されているかを確認する
まずここから始めます — 現在の永続化の状態を取得します:
redis-cli INFO persistence
注目すべきフィールド:
rdb_bgsave_in_progress:1 # 1 = セーブが現在実行中
rdb_current_bgsave_time_sec:42 # セーブ開始からの経過秒数
rdb_last_bgsave_status:ok # 最後に完了したセーブのステータス
aof_rewrite_in_progress:0 # 1 = AOFの書き換えがフォーク中
rdb_bgsave_in_progress:1はセーブがアクティブであることを意味します。待ちましょう。完了するとRedisは0に戻します。
ステップ2 — 適切な方法で待つ
セーブが完了するまでポーリングします:
watch -n 2 'redis-cli INFO persistence | grep -E "bgsave_in_progress|bgsave_time_sec|last_bgsave"'
通常のディスクI/Oで数GB未満のデータセットであれば、BGSAVEは数秒から数分で完了します。5〜10分を過ぎても終わらない場合は問題があります — 以下のトラブルシューティングのセクションに進んでください。
rdb_bgsave_in_progressが0になったら、BGSAVEを再実行します:
127.0.0.1:6379> BGSAVE
Background saving started
ステップ3 — 最初のセーブをトリガーしたものを特定する
Redisのログを見れば、何がきっかけになったか正確にわかります:
tail -n 100 /var/log/redis/redis-server.log | grep -i "bgsave\|save\|fork"
よく見られるログの行:
# 手動によるBGSAVE
* Background saving started by pid 12345
# saveルールによるトリガー(例:save 300 10)
* 10000 changes in 300 seconds. Saving...
# レプリケーションによるトリガー
* Starting BGSAVE for SYNC with target: disk
レプリケーションによるセーブの場合は、レプリカの接続を確認します:
redis-cli INFO replication | grep -E "connected_slaves|slave"
ステップ4 — BGSAVEに時間がかかりすぎている場合
セーブが遅い原因は3つに絞られます。順番に確認していきましょう。
ディスクが遅い大容量のデータセット
子プロセスはデータセット全体をディスクに書き込みます — すべてのバイトを。100 MB/sの速度のHDDで10 GBのデータセットがある場合、オーバーヘッドを考慮する前から少なくとも100秒かかります。データセットのサイズとディスクのスループットを確認しましょう:
redis-cli INFO memory | grep used_memory_human
iostat -x 1 5 # ディスク使用率を確認
ディスクI/Oが上限に達しているとセーブは遅くなります。RDBファイルをより高速なボリュームに移動しましょう:
# redis.conf
dir /mnt/fast-ssd/redis
高い書き込み負荷によるコピーオンライトの圧迫
フォーク後、Redisへの書き込みのたびにOSは影響を受けるメモリページをコピーします(コピーオンライト)。セーブ中に書き込みスループットが高いと、子プロセスのワーキングセットが膨らみます。コピーされるページが多いほど、セーブに時間がかかります。最後のセーブからどれだけ変更があったか確認しましょう:
redis-cli INFO stats | grep rdb_changes_since_last_save
数値が大きい場合、セーブの間隔が広すぎます。間隔を短くするか、オフピーク時間帯にセーブをスケジュールしましょう。
Transparent Huge Pagesによるフォークのレイテンシ
THPはRedisのパフォーマンスに悪影響を与えることで知られており、コピーオンライトのオーバーヘッドを大幅に増加させます。確認して無効化しましょう:
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always]と表示されている場合は無効化する:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
ステップ5 — 競合を避けるためにセーブ頻度を調整する
高負荷なサーバーでは、頻繁な自動セーブが主な原因です。デフォルトのルールは積極的に実行されるため、大きなデータセットではセーブが重複することがあります。redis.confで設定を緩めましょう:
# デフォルト(大容量データセットには頻度が高すぎることが多い)
save 900 1
save 300 10
save 60 10000
# より保守的 — 大きな変更量があった時だけ保存
save 900 1
save 300 1000
再起動なしで適用する:
redis-cli CONFIG SET save "900 1 300 1000"
ライブ設定が反映されたか確認する:
redis-cli CONFIG GET save
確認
セーブが正常に完了したことを確認します:
redis-cli INFO persistence | grep -E "rdb_bgsave_in_progress|rdb_last_bgsave_status|rdb_last_save_time"
正常な出力は次のようになります:
rdb_bgsave_in_progress:0
rdb_last_bgsave_status:ok
rdb_last_save_time:1714900000
次に、RDBファイル自体が更新されていることを確認します:
ls -lh $(redis-cli CONFIG GET dir | tail -1)/$(redis-cli CONFIG GET dbfilename | tail -1)
タイムスタンプが直近のセーブと一致しているはずです。
クイックリファレンス
- 無視して問題ないのはセーブが既に実行中の場合 — 完了するまで待つだけ
- 状態の確認:
redis-cli INFO persistence | grep bgsave - BGSAVEの子プロセスを手動でkillしてはいけません — 何時間も実行中でない限り、書き込み途中でkillするとRDBファイルが破損します
- LASTSAVEは最後に成功したセーブのUnixタイムスタンプを返します:
redis-cli LASTSAVE - すべてのセーブを完全に停止するには:
redis-cli CONFIG SET save ""— どの永続化を諦めるか理解した上でのみ実行してください

