[Dd]enzow(ill)? with DB and Python

DBとか資格とかPythonとかの話をつらつらと

Redisの永続化とパフォーマンスのバランスを考えてみる(redis-pyを添えて)

ISUCONに関わって初めて真面目にRedisと向かい合ってます。RedisはオンメモリのKVSですが、デフォルトの設定では、再起動後もその内容が失われないように永続化されています。しかし、ISUCONというパフォーマンス重視の環境で永続化を考える場合、いくつか検討事項があるように思います。

Redisの永続化

Redisが備えている永続化の機能はRDBAppend-only file(AOF)の2種類です。RDBMS脳な人(自分を含め)向きにいうならば、RDBはExportした論理ダンプでAOFはWALファイルやREDOログと呼ばれるものを取り続けるイメージでしょうか。

type メリット デメリット
RDB 取得が容易、読み込みが高速 取得後からクラッシュまでの間にデータロスが発生し得る
AOF 可用性を最大にすればデータロスがほぼない パフォーマンスへの影響がRDBより大きい

AOFの場合は、更新処理ごとにその処理内容を保存し続けることでデータが再現できるようにするため、更新系の処理のパフォーマンスがかなり下がります。デフォルトではappendfsync everysecが設定されており、1秒毎に処理内容を書き出します。そのため、極端にパフォーマンスが下がるわけではありません。

また、RDB形式の場合もデフォルトではある程度更新を行うと自動で取得されるようになっています。例えば

save 300 10

と合った場合は、10回更新すると300秒以内にスナップショットを取得するという設定になります。大量の更新をかけている場合は頻繁にスナップショットが取得されることになります。

RDBやAOFファイルが有る場合、Redisは起動時にそのファイルを読み込むため再起動後でもデータが保持されたままになります。

ISUCONではどうすべきか

恐らく課題の内容にもよりますので一概には言えませんが、AOFであれRDBであれパフォーマンスへの影響がある以上は最低限の取得に抑えたいというのが基本方針です。再起動に耐えられなくろも良いみたいな要件があるのであれば喜んで以下を設定します。

save ""  # 一切RDBのスナップショットを取らない

appendonly no  # AOFは取得しない

この設定の場合、Redisを再起動するとすべてが消えます。しかし、投入されたデータを保持する必要があるならば、最低限のタイミングでRDB形式のスナップショットを取得するのが楽でしょう。

例えば、ベンチ-マークが終了したあとにアプリケーション側からSAVEコマンドを発行するのはシンプルでしょう。redis-pyであれば以下で可能です。

r = redis.StrictRedis(host='192.168.56.100', port=6379, db=0)
r.save()
print("complete")

saveはスナップショットの取得が完了するまで他の処理をブロックします。そのため、ベンチ-マーク中に呼び出すと著しく影響が出ます。あくまで、処理完了後のタイミングが特定できている場合に使用すべきでしょう。

一方で、BGSAVEは非同期にスナップショットを取得し他の処理をブロックしません。もちろんI/Oがありますので影響はありますが、SAVEに比べれば限定的です。なお呼び出した時点ですぐに操作が戻るため、実際にバックアップが完了したかはlastsaveで保存時刻を確認する必要があります。

redis-pyでは以下の様に実行します。

r = redis.StrictRedis(host='192.168.56.100', port=6379, db=0)
last_timestamp = r.lastsave()
r.bgsave()
while last_timestamp == r.lastsave():
    print("not yet.")
print("complete")

bgsave発行前のlastsaveの時刻と比較することで完了タイミングを確認することが可能です。なお、楽観的にいくのであればlastsaveは見ないでbgsaveを発行したままにしてもいいでしょう。

まとめ

確実にバックアップを取得すべきタイミングがわかっており、それがベンチマーク後にできるのであればsaveでサクッと取得してしまうのが楽でしょう。そうでないならば、スレッドなりを作成してbgsaveを許容できる範囲で定期的に投げるなどの方法がベターではないでしょうか。