Redis の RDB 永続化

Redis には、RDB (Redis Database) と AOF (Append Only File) という 2 つのデータ永続化方式があります。

RDB 永続化は、ある時点で現在のデータのスナップショットを作成して、RDB ファイルとしてディスクに保存し、災害によるデータ損失を防ぐことができます。

RDB ファイルさえあれば、Redis はそれを使ってデータを復元することができます。Redis サーバを起動する際、RDB ファイルの存在を検出すると自動的に読み込まれます。

RDB 永続化はsaveオプションを設定することで、一定時間内に一定数の変更があったら、Redis は自動的にBGSAVEコマンドを実行します。例えば save 60 1000は、60 秒以内に少なくとも 1000回の変更が行われたら、BGSAVEコマンドが実行されます。 手動でSAVEコマンドまたはBGSAVEコマンドを実行することもできます。

  • SAVEコマンドは、データベースを同期的にディスクに保存します。RDB ファイルが作成されるまでサーバプロセスをブロックします。

  • BGSAVEコマンドは、データベースを非同期的にディスクに保存します。fork()で子プロセスを生成し、子プロセスが RDB ファイルの作成を行い、親プロセス(サーバプロセス)は引き続きクライアントからの要求を処理し続けます。

BGSAVEコマンドはサーバプロセスをブロックしないため、サーバ設定のsaveオプションを通じて自動的に実行させることができます。

Redis のデフォルト設定では、RDB 永続化が有効になっています。save ""で無効にすることも可能です。 RDB 永続化を有効にしたままにする場合は、十分なメモリとディスク容量を確保する必要があります。そうしないと障害が発生する可能性が高いです。

十分なメモリ容量を確保する

BGSAVEコマンドの実行で子プロセスが生成された時、COW (Copy-On-Write) のおかげで、親プロセスのメモリ空間をコピーするのではなく、共有します。

しかし、書き込み操作が頻繁に行われた場合、書き込み内容を反映させるために大量の使用可能なメモリが必要になる場合があります。Redisサーバダウンを防ぐため、マシンのメモリが、Redis のデータ保存用メモリの 2 倍以上ある方が安全です。

メモリが不足になっている場合、Redis は OOM Killer に強制終了される可能性があります。

メモリ容量不足の場合

Redis サーバを立ち上げるマシンでは、直接ホストで Redis サーバを起動し、systemd によって管理されます。 systemctl コマンドで Redis の状態を確認すると、Redis が異常終了されました。

$ sudo systemctl status redis
● redis.service - Redis persistent key-value database
   Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/redis.service.d
           └─limit.conf
   Active: failed (Result: signal) since Wed 2024-01-10 18:14:25 JST; 29s ago
  Process: 8190 ExecStop=/usr/libexec/redis-shutdown (code=killed, signal=TERM)
  Process: 8254 ExecStart=/usr/bin/redis-server /etc/redis/redis.conf --daemonize no --supervised systemd (code=killed, signal=KILL)
 Main PID: 8254 (code=killed, signal=KILL)
   Status: "Ready to accept connections"

journalctl コマンドで systemd のログを確認し、OOM Killer によって Redis プロセスが強制終了されたことがわかります。

$ journalctl -b | less

以下はログの一部からの抜粋です。

Jan 10 18:14:24 localhost kernel: oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=ec33a5e6da59ed697cdbc803f32a79b7d609ed286ce656c387a6dd
0332239063,mems_allowed=0,global_oom,task_memcg=/system.slice/redis.service,task=redis-server,pid=8254,uid=995
Jan 10 18:14:24 localhost kernel: Out of memory: Killed process 8254 (redis-server) total-vm:15947752kB, anon-rss:11803720kB, file-rss:0kB, shmem-r
ss:0kB, UID:995 pgtables:25756kB oom_score_adj:0
Jan 10 18:14:25 localhost systemd[1]: redis.service: main process exited, code=killed, status=9/KILL

十分なディスク容量を確保する

子プロセスがデータベースを RDB ファイルに保存する際に、まずtemp-123.rdbのようなファイル名の末尾にプロセス ID が付いている一時ファイルを作成し、データをそのファイルにダンプします。正常にダンプされた場合、一時ファイルの名前を dump.rdb に変更します。

つまり、バックアップには現在のデータの約 2 倍のディスク容量が使用されます。 その時、ディスク容量が不足していると、バックアップができなくなり、エラーが発生し、Redis にデータを書き込むことができなくなります。

ディスク容量不足の場合

Redisサーバのログで、ディスク容量不足のエラーが出ています。

1:M 10 Jan 2024 15:56:24.081 * 100 changes in 300 seconds. Saving...
1:M 10 Jan 2024 15:56:24.117 * Background saving started by pid 31
31:C 10 Jan 2024 15:57:02.745 # Write error saving DB on disk: No space left on device
1:M 10 Jan 2024 15:57:03.019 # Background saving error

クライアント側 (redis-py) では、以下のような ResponseError になります。Redis の設定stop-writes-on-bgsave-errorオプションのデフォルト値は「yes」になり、バックアップが失敗すると、書き込み操作ができません。

redis.exceptions.ResponseError:: MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.


参考:https://redis.io/docs/management/persistence/

Tags:

Updated: