NHN Cloud Meetup 編集部
Redis 6の新機能と主要機能のテスト
2020.08.11
4,107
はじめに
4月30日にRedisの新機能が発表されました。Redisオープンソースの創始者であるAntirez氏は、今回のバージョン6がこれまでのリリースの中で最大の変化であると公言しています。一体どのような変化があったのか調べてみましょう。
Redis 6 is the biggest release of Redis *ever*, so even if it is stable, handle it with care, test it for your workload before putting it in production.
( http://antirez.com/news/132 )
クライアントサイドキャッシュ
データベースのパフォーマンスを最適化し、Redisのようなスピーディーなキャッシュサーバーを使用しても満足のいく性能が出ないときは、クライアントサイドキャッシュの導入を検討されると思います。Redis 6では、この機能を非常に簡単に使用することができます。
デフォルトで、データベースからデータを取得する方法は上図のようになります。ここでのデータベースは、Oracle、Redisなどが考えられます。しかし、クライアントサイドキャッシュを追加すると、アプリケーションがネットワークを通じてデータベースに問い合わせる時間を短縮させるだけでなく、データベースからリクエストを受けて処理するコストも削減することができます。
しかし、キャッシュはデータの整合性に対してデメリットがあります。他のクライアントでuser:1234の値を変更したとき、ローカルに保存されているデータはなくならなければなりません。このため、Redisはそれぞれメリットとデメリットが存在する2つのモードを提供しています。
基本モードでは、Redisサーバーはどのクライアントがどのキーを保存しているかを記憶するInvalidation Tableを作成します。クライアントからここに保存されたキーを変更すると、Redisはキーを保存している他のすべてのクライアントにキャッシュされた値を削除するように要請します。この過程で、Redisサーバーのメモリ使用量はやや増加することがあります。
Invalidation Tableがメモリを過度に使用しないように、Redis 6ではtracking-table-max-keysというパラメータを導入しています。この値は、Invalidation Tableが保存できるキーの数で、基本的に100万個まで保存することができます。
もう1つのモードは、ブロードキャストモードです。このモードでは、Redisサーバーはキーの値を保存しません。代わりに、キーの前のプレフィックスと合致するキーを持つクライアントを保存します。プレフィックスに該当するクライアントは、該当するキーが変更されるたびにサーバーから通知を受け、合致するキーがある場合はキャッシュされている対象のデータを削除します。この方法は基本モードよりも使用されるメモリが少ない一方で、該当するキーが存在しないクライアントもこの値を受信するため、ネットワーク使用量が増加することがあります。
以前、この内容をTOASTブログで紹介した際に、下記のような質問をいただきました。
Q. HGET/HSETコマンドもクライアントサイドキャッシュを適用できますか?
A.はい。関連する公式文書はありませんが、テストした際、HGET、HSETを含むさまざまなコマンドに適用されていました。HLENで長さのみ読み込んで、他のクライアントがHSETでハッシュ内のアイテムを変更しても、invalidメッセージが送信されます。
Q.では1000万個のアイテムが入っているハッシュをクリアすると、invalidメッセージがたくさん出力されるかもしれませんね。その場合、パフォーマンスにかなり影響すると思いますが、懸念したように動作するかはテストしてみる必要がありますね。
この質問に答えるため、下記のようなテストを実施しました。
2.他のクライアントでmyhashを修正
上記のシナリオでは、通知メッセージが何通届くでしょうか。修正したアイテム数だけ届くでしょうか?それともひとつのキーにつき1回送信されるでしょうか?
テスト結果は、上記のとおりでした。複数のアイテムが含まれるハッシュキーが削除/変更されても、invalidateメッセージは1回のみ発生しました。幸いなことに、質問された方が心配されていたような大量通知は発生しませんね。
Threaded I/O
Redisはこれまでシングルスレッドで動作してきました。複雑性を軽減しデータを不可分操作できるというメリットがあります。しかし、シングルスレッドであるために発生するデメリットも存在しました。時間のかかる1つのジョブが他の多くのジョブを待機させてしまったり、ユーザーが誤って実行したコマンドにより障害が発生する事例がたくさんありました。
今回のバージョンから、Redisクライアントに対してread/writeスレッドを構成できるようになりました。この機能は、io-threadsというパラメータで制御され、デフォルトは1、すなわち以前のようにシングルスレッドのみを使用します。公式文書では、この機能を慎重に使用するように注意喚起しています。
- パフォーマンスの問題がある場合にのみ、この機能を使用してください。
- 少なくとも4つの基本コアと、1つ以上の予備コアが存在するマシンで、この機能を使用してください。
- コアが4つなら2~3個のスレッドを、8つなら6個のスレッドを使用することを推奨します。
- 基本的に時間がかかる書き込み操作が別のスレッドで動作するようにし、O(1)やO(log(N))のように速くクエリされる既存クエリは、メインスレッドで動作します。
- io-threads-do-reads というパラメータを使って読み込んでもスレッドを使用できますが、大きな性能向上は見られないので使用は推奨しません。
Antirez氏は、複雑性を軽減するためにこれまでスレッドを導入しなかったと理由を説明し、この機能を導入することは、デザインの観点から大きな犠牲を払ったとツイートしています。それでも200行ほどのコードを追加することで、2.5倍程度の性能向上を目指すことができたそうです。
この内容を実際にテストした事例を探してみたところ、実際は性能が5〜10%程度、落ちるそうです。スレッドを使って望む結果を得るためには、もう少しテストが必要だと思われます。
ACL
データベースを運営する立場からRedisに必要であると考えられていた機能が、今回のバージョンで導入されました。MySQLやOracleには、ユーザーが存在し、そのユーザーが使用できるコマンドとアクセス可能なテーブルを指定することができましたが、Redisには最初からユーザーという概念が存在していませんでした。しかし、rename-commandを使って特定のコマンドを使用できないように迂回する方法がありましたが、文字通り迂回するという方法だけでした。
Redis 6ではユーザーを以下のように定義できます。
- 名前
- パスワード
- アクセス可能なキーのパターン
- 実行可能なコマンド/コマンドのカテゴリ/サブコマンド
バージョン6からパスワードはSHA256によりハッシュされて保存されます。また、ACL GENPASSコマンドを使って強力なパスワードを作成することもできます。
その他の細かいアップグレード
- レプリケーションの過程で使用されたRDBファイルをすぐに削除できるようになりました。特定の環境では、データをディスクに保存せずにメモリにのみ保存するのが有用な場合があるからです。
- レプリケーションで使用されるプロトコルの性能が向上しました。Full Syncを削減し、Partial Syncをより頻繁に使用できるようになりました。
- BLPOPなどの秒と関連するコマンドで「秒」を処理する過程が改良されました。
- RDBをロードする速度が速くなり、複数のクライアントで接続してもINFOデータを素早く表示することができます。
- 文字列からLCS(Long Common Subsequence)を確認できるSTARGO
コマンドが追加されました。コロナウイルスにおけるRNAを比較する目的で使うことができます。