このページでは Redis の永続化について、技術的な説明を提供します。すべての Redis ユーザーはこの内容に目を通しておくことを推奨します。Redis の永続化と障害耐性の保証について、より広範に概観するため Redis persistence demystified も参照すると良いでしょう。

Redis の永続化

Redis は、守備範囲の異なる永続化オプションを提供します:

もっとも重要なことは、RDB と AOF の、異なるトレードオフについて理解することです。RDB から始めましょう:

RDB の利点

  • RDB は、ある時点の Redis データを表現した、非常にコンパクトな 1 つのファイルです。RDB ファイルはバックアップに最適です。たとえば、直近 24 時間については 1 時間ごとに RDB ファイルをアーカイブし、直近 30 日については日ごとに RDB スナップショットを保存する、などです。これにより、災害時において、データセットの異なるバージョンを簡単にリストア可能です。

  • RDB は 1 つのコンパクトなファイルであり、遠隔のデータセンター、ないし Amazon S3 (おそらく暗号化されるでしょう) へと転送できるため、災害復旧にとても適しています。

  • RDB は Redis の性能を最大化します。なぜなら、永続化の際に親プロセスが行う必要があるのは子プロセスを fork するだけであり、残りの仕事はすべて子プロセスが受け持つためです。親のインスタンスがディスク I/O その他を実行することはありません。

  • RDB は、大きなデータセットの場合に、AOF よりも高速な起動を可能にします。

RDB の欠点

  • Redis が停止した際(電源遮断の後など)、データロスの機会を最小化したい場合には RDB は適しません。RDB が生成される、異なる セーブポイント を設定することができます(たとえば、少なくとも 5 分間に 100 の書き込みがデータセットに発生した後、など。ただしセーブポイントは複数指定できます)。しかし、通常においては RDB スナップショットは 5 分ごと、もしくはもっと長い間隔で作成されるため、何らかの理由で Redis が正しくシャットダウンされずに停止した場合、最後の数分のデータを失うことを予期しておかないといけません。

  • RDB は 子プロセスを使ってディスクに永続化を行うため、頻繁に fork() する必要があります。Fork() はデータセットが大きいと時間がかかるため、もしデータセットが非常に大きく、CPU性能があまり良くないと、数ミリ秒、悪くて 1 秒程度にわたって Redis はクライアントに応答するのを中断します。AOF も fork() を必要としますが、耐障害性を損うことなく、どのくらいの頻度でリライトを行うかを調整することができます。

AOF の利点

  • AOF を使うと、Redis はより障害に強くなります。異なる fsync ポリシーを選択できます: fsync を全く行わない、毎秒 fsync を実行する、すべてのクエリごとに fsync を実行する。デフォルトポリシーの「毎秒 fsync を実行する」を使うことで、書き込みをロスする可能性のある時間を 1 秒以内に抑えながら、高い書き込み性能を維持できます(fsync はバックグラウンドプロセスを使って実行され、fsync が実行中でない間、メインスレッドは書き込みを行うように最大限努めます)。

  • AOF ログは追記専用のログであるため、シークがなく、電源遮断による破損の問題も発生しません。何らかの理由によりログが書き込みコマンドの途中で終わっていた場合でも(ディスクフルその他の原因により)、redis-check-aof ツールで簡易に修復可能です。

  • AOF が肥大化してくると、Redis は自動的にバックグラウンドでリライトを行います。Redis が継続して古いファイルに追記している間も、リライトは完全に安全です。現在のデータセットを作成するのに必要な最小限の操作を記録した、まったく新しいファイルが作成されます。この、2 つめのファイルの準備が整ったら Redis は 2 つのファイルを入れ替え、新しいほうに追記を始めます。

  • AOF はすべての操作を順々に、理解しやすくパースしやすいフォーマットで保持しています。AOF ファイルは簡単にエクスポートできます。たとえば、手違いで FLUSHALL コマンドを発行してすべてを消去してしまった場合でも、その間でリライトが発生していなければ、サーバーを停止して最後のコマンドを削除し、Redis を再起動することでデータセットを救うことができます。

AOF の欠点

  • AOF ファイルは通常、同じデータセットを表す等価な RDB ファイルよりも大きくなります。

  • AOF は、正確な fsync ポリシーにより RDB よりも遅くなることがあります。一般的には fsync を 毎秒 に設定しておくことで高い性能を維持でき、また、fsync を無効にすると、非常に高負荷な状況下でも RDB とまったく同等の速度になります。しかしながら、RDB は書き込み負荷が非常に高い場合においても、最大レイテンシを保証することが可能です。

  • 過去に、特定のコマンド(たとえば、BRPOPLPUSH のようなブロッキングコマンド)において、データリロード時に、AOF が正確なデータセットを再生成しない、という稀なバグがありました。このバグは稀なもので、また、私たちはランダムで複雑なデータセットを自動生成してリロードし、すべてが問題ないかをチェックするテストスイートを用意しています。この点をよりクリアにします: Redis AOF は、MySQL や MongoDB のように、現在の状態をインクリメンタルにアップデートする働きをします。一方で、RDB スナップショットは都度都度、スクラッチからすべてを生成するため、理論上より頑健です。しかし、

  1. Redis により AOF がリライトされる都度、それはデータセット中の実際のデータから、スクラッチで再生成されます。これにより、常にひとつの AOF に追記し続ける(または、メモリ上のデータではなく、古い AOF を読んでリライトする)のと比較すると、バグに強くなります。この点は強調しておくべきでしょう。

  2. 私たちは、実環境において、AOF が破損したという報告をユーザーから受けたことは 1 度もありません。

よくわかった、 それでは、私はどちらを使ったらいいのか?

一般的に言って、PostgreSQL が提供するのと同等レベルのデータ安全性を確保したいときは、両方の永続化メソッドを併用することを推奨します。

扱うデータが多く、災害時において数分間のデータロスを許容できるなら、単に RDB のみを使うと良いでしょう。

多くのユーザーが AOF のみを使っていますが、私たちはこのやり方を推奨しません。なぜなら、その時々の RDB スナップショットはデータベースのバックアップに最適で、再起動が高速にできるためです。また、AOF には万一のバグがありえます。

追記: これらの理由により、私たちは将来的に(長期プランとして)、AOF と RDB をひとつの永続化モデルに統合することを検討しています。

以下のセクションでは、2 つの永続化モデルについてより詳細を記述します。

Snapshotting

デフォルトで、Redis は dump.rdb と呼ばれるバイナリファイル内に、データセットのスナップショットを保存します。データセットに対して、毎 N 秒ごとに少なくとも M 回の変更があったら保存を行うよう、 Redis の設定が可能です。または、手動で SAVE および BASAVE コマンドを発行できます。

たとえば、次の設定では、60 秒毎に少なくとも 1000 個のキーの変更があったら Redis はデータセットをディスクに保存します。

save 60 1000

この戦略は snapshotting として知られています。

これはどのように動作するか

Redis がデータセットをディスクにダンプする必要があるときはいつでも、以下の処理が行われます:

  • Redis は fork を実行します。子プロセスと、親プロセスが動いている状態になります。

  • 子プロセスはデータセットを一時 RDB ファイルに書き出し始めます。

  • 子プロセスは新しい RDB ファイルの書き出しを終えると、古いものと置き換えます。

この方法により Redis は、copy-on-write セマンティクスの恩恵が受けられます。

Append-only file

スナップショットは障害に強くありません。Redis が動いているコンピューターが停止したら、電源で問題が発生したら、または事故でインスタンスを kill -9 してしまったら、Redis に書かれた直近の変更は失われます。これはある種のアプリケーションにとっては大きな問題にならない一方、完全な耐障害性が必要となるユースケースもあります。こうしたケースにおいては、Redis は実行可能な選択肢ではありません。

追記専用ファイル は、Redis で完全な耐障害性を実現するための、代替の戦略です。バージョン 1.1 から利用できるようになりました。

設定ファイルで AOF を有効にできます:

appendonly yes

以降、Redis はデータセットを変更するコマンド(e.g. SET)を受け付ける都度、AOF に追記します。Redis を再起動したときは、AOF ファイルをリプレイして状態を再構成します。

ログのリライト

すぐに推測できるように、書き込み操作が実行されるに応じて、AOF はどんどん大きくなります。たとえば、あるカウンターを 100 回インクリメントした場合、最終的にデータセット内には最後の値しか含まれないですが、AOF には 100 個のエントリが含まれることになります。そのうち 99 エントリは、現在の状態を再構成するためには不要なものです。

そのため、Redis は興味深い機能をサポートしています: クライアントへのサービスを中断することなく、バックグラウンドで AOF の再構築を行います。 BGREWRITEAOF コマンドを発行すると、Redis は、メモリ上の現在のデータセットを再構成するのに必要な、最小限のコマンドシーケンスを書き出します。もし Redis 2.2 で AOF を使っていたら、時々 BGREWRITEAOF コマンドを自分で実行する必要があります。Redis 2.4 以降、ログの自動リライトのトリガーを設定できるようになりました(詳しくは 2.4 [訳注:以降] の設定ファイルを参照してください)。

追記専用ファイルは、どの程度の障害耐性がありますか?

Redis が、データをディスクに fsync する頻度を設定できます。3 つのオプションがあります:

  • 新しいコマンドが AOF に追加される都度、 fsync を実行します。とても遅く、とても安全です。

  • 毎秒 fsync を実行します。十分に高速(バージョン 2.4 ではスナップショットと同程度に高速)ですが、災害(事故)の際は 1 秒間のデータを失う可能性があります。

  • fsync を実行せず、オペレーティングシステムに任せる。もっとも高速で、もっとも安全性の低い方法です。

推奨(かつ、デフォルト)は毎秒 fsync を実行するポリシーです。これは非常に高速で、かつ十分に安全です。 always ポリシーは現実的に大変遅いです(Redis 2.0 で改善はされていますが)。 fsync それ自体を速くする方法はありません。

もし AOF が破損したらどうしたらいいか?

AOF ファイルを書き込み中にサーバーがクラッシュし(ただし、一貫性が保たれなくなることはありません)、ファイルが破損して Redis がロードできなくなってしまう可能性があります。こうした場合は、以下の手順で修正を実施してください:

  • AOF ファイルのバックアップコピーを取得する。

  • 元のファイルに、Redis に同梱されている redis-check-aof ツールを適用して修正する:

    $ redis-check-aof –fix

  • 必要に応じて、 diff -u で 2 つのファイルの差分をチェックする。

  • 修正したファイルを使ってサーバーを再起動する。

これはどのように動作するか

ログリライトは、スナップショットで使っているのと同じ copy-on-write トリックを使っています。これは以下のように動作します:

  • Redis は fork を実行し、子プロセスと親プロセスが動いている状態になります。

  • 子プロセスは、新しい AOF を一時ファイルへ書き出し始めます。

  • 親プロセスは、すべての新しい変更をメモリ上のバッファに蓄えます(同時に新しい変更は古いファイルに書き続けられるため、リライトが失敗した場合も安全です)。

  • 子プロセスがリライトを完了したら、親はシグナルを受け取り、メモリ上のバッファを子プロセスが生成したファイルの末尾に書き出します。

  • Profit! Redis は古いファイルをアトミックに新しいファイルにリネームし、新しいファイルに追記を始めます。

現在 dump.rdb スナップショットを使っているが、AOF に変更するにはどうしたらいいか?

Redis 2.0 と 2.2 [訳注: 以降]で手順が異なります。2.2 のほうがシンプルで、再起動が必要ありません。

Redis >= 2.2

  • 直近の dump.rdb ファイルをバックアップします。

  • バックアップを安全な場所に退避します。

  • 以下の 2 つのコマンドを発行します:

  • redis-cli config set appendonly yes
  • redis-cli config set save “”
  • データベースが、[訳注: コマンド実行前と]同じ数のキーを含んでいることを確認します。

  • 書き込みが、追記専用ファイルに正しく書き出されていることを確認します。

1 つめの CONFIG コマンドは、追記専用ファイルを有効にします。このとき、初期ダンプファイルを生成するために Redis はブロックします 。そして、書き込みのためにファイルをオープンし、以降の書き込みリクエストを追記し始めます。

2 つめの CONFIG コマンドはスナップショット永続化を無効にしています。これはオプションで、両方の永続化メソッドを有効にしておくこともできます。

重要: redis.conf で AOF の設定を有効にしておくことを忘れないでください。そうしないと、サーバーを再起動したときに設定の変更が失われ、古い設定で起動します。

Redis 2.0

  • 直近の dump.rdb ファイルをバックアップします。

  • バックアップを安全な場所に退避します。

  • データベースへの書き込みをすべて停止しておきます!

  • redis-cli bgrewriteaof を発行する。これで追記専用ファイルが作成されます。

  • Redis が AOF ダンプを完了したら、サーバーを停止します。

  • redis.conf を編集し、追記専用ファイルへの永続化を有効にします。

  • サーバーを再起動します。

  • データベースが、[訳注: コマンド実行前と]同じ数のキーを含んでいることを確認します。

  • 書き込みが、追記専用ファイルに正しく書き出されていることを確認します。

AOF と RDB 永続化の相互作用

Redis 2.4 以降、RDB スナップショット操作がすでに実行中であれば AOF リライトトリガーを避け、また AOF リライトが実行中であれば BGSAVE は許可されません。これは 2 つのバックグラウンドプロセスが同時に走ることで、重いディスク I/O が発生することを避けるためです。

スナップショット実行中に、ユーザーが明示的に BGREWRITEAOF を発行した場合、サーバーは OK ステータスコードを返します。ユーザーの操作はスケジューリングされ、スナップショットが完了したらリライトが開始されます。

AOF と RDB の両方が有効化されている場合、Redis は再起動時に元データを復元する際、AOF ファイルを使用します。そのほうがより完全に近いことが保証されているためです。

Redis データのバックアップ

この説を読む前に、次のセンテンスを必ず読んでください: データベースのバックアップを確実にとりましょう 。ディスクは壊れるものであり、クラウド上のインスタンスは消失するものであり、その他もろもろ: バックアップをとらないことは、データを /dev/null へ送り込む大きなリスクを伴います。

稼動中にRDB ファイルのコピーが取得できるため、Redis は非常にバックアップ・フレンドリーです: RDB は一度生成されると変更されません。また、書き込み中は一時的な名前[訳注: ファイル名]が使われ、スナップショットが完了した場合に初めて rename(2) によりアトミックにリネームされます。

これは、サーバー稼動中に RDB ファイルをコピーしても完全に安全であることを意味します。以下は、推奨される方法です:

  • あるディレクトリに、時間ごとの RDB ファイルのスナップショットを作成し、別のディレクトリには日ごとのスナップショットを作成する cron ジョブを用意する。

  • cron スクリプトを実行する都度、 find コマンドを使って古いスナップショットを削除する: たとえば時間別のスナップショットは 48 時間分だけ残し、日別のスナップショットは 1 ヵ月ないし 2 ヵ月分だけ残す、など。スナップショットのファイル名には日時を入れましょう。

  • 少なくとも 1 日に 1 回は、 データセンターの外 か、少なくとも Redis インスタンスが稼働している 物理マシンの外 へRDB スナップショットを転送しましょう。

災害時の復旧

Redis において、災害時の復旧は、基本的にはバックアップと同義ですが、バックアップを異なる複数のデータセンターへ転送できるようにしておく、という点が加わります。こうしておくことで、何らかの壊滅的な被害が Redis が稼働しているメインのデータセンターに発生した場合でも、データは守られます。

Redis を使っているユーザーの多くはスタートアップ環境にあり、多くの資金を持っていません。そのため、高コストすぎないリカバリーテクニックを見ていきます。

  • Amazon S3 やその他の類似サービスは、災害復旧システムを実装するにあたって良い選択肢です。単純に、日毎、または時間毎の RDB スナップショットを暗号化して S3 に転送してください。 gpc -c で暗号化することができます(共通鍵暗号方式)。パスワードを、複数の異なる安全な場所に保管しておきましょう(たとえば、あなたの組織でもっとも重要な人物にコピーを渡しておくなど)。より安全性を高めるなら、複数のストレージサービスを使うと良いでしょう。

  • SCP を使って遠隔のサーバーにスナップショットを転送してください。大変シンプルで安全な経路があります: 遠隔地に小さな VPS を用意し、SSH をインストールし、パスフレーズなしで SSH クライアントキーを生成し、公開鍵を VPS 上に配置します。これでバックアップを自動転送する準備が整いました。少なくとも 2 つの VPS を 2 つの異なるプロバイダ上に置いておくのが最良でしょう。

このシステムは、正しく実装されなければ簡単に失敗してしまう、ということを理解しておくことが大事です。少なくとも転送完了後には、ファイルサイズを必ず検証してください(コピー元のファイルと一致するはずです)。もし VPS を使っているなら SHA1 ダイジェストも検証すると良いでしょう。

また、最新のバックアップが何らかの理由で失敗していないか、独立した異常検知システムが必要になるでしょう。