Carpe Diem

備忘録

RedisのPipeline, Multi, Watchの区別

概要

RedisのPipeline, Multi, Watchの区別をする上であまりシーケンス図を用いた説明がなかったので図示してみました。

解説

Pipeline

課題

通常複数の処理を実行しようとすると以下のような流れになります。
シンプルではありますが都度サーバ通信が入るところに無駄があります。

解決策としてのPipeline

パイプライン処理は複数の処理を1つにまとめてRTT(Round Trip Time)を改善します。

先程3回サーバ通信が必要でしたが、Pipelineを用いることで1回で済むようになっています。

Multi

課題

先程のPipelineは複数の処理をバッチ化できるというメリットがありましたが、途中で他のクライアントからの処理が割り込む可能性もあります。

上図ではまだGETなので大丈夫ですが、途中割り込んでしまうとデータ整合性が壊れてしまうケースももちろんあります。

解決策としてのMulti

割り込みによるデータ不整合を回避したい場合は、Multiを使って割り込みをさせないようにします。

Redisは基本シングルスレッドなアプリケーションなので、処理自体が直列化化されています。なので実行順さえ保証できればデータの整合性を保証できます。

直列化(Serializability)については以下の記事を参考にしてください。

christina04.hatenablog.com

Watch

課題

PipelineもMultiもですが、Redisに直接送るのはEXEC後なため途中で読み出したデータを使って意思決定をすることはできません

この図のようにGET Aしてもその時点では値がnilとして返るため、それを使うと以降のデータや意思決定がおかしくなります。

スコープ外で読み出す

そこでMultiスコープ外で読み出す必要があります。

このやり方であれば読み出したデータを使うことができます。

整合性が保証できない

しかし今度は読み出した後でデータが変更され、Multiでデータ整合性を保証できないケースが出てきます。
下の例では本来AをBやCにコピーしたかったものの、処理をしている間にAがインクリメントされてしまって値が変わってしまいました。

解決策としてのWatch

Watchは楽観ロックの仕組みで、あらかじめ特定のkeyに他のクライアントからの変更が入らないか監視しておき、EXEC時に変更があればエラーを返す仕組みです。

悲観ロックのようにクライアントBの処理をブロックすることはありませんが、不整合を検知することはできます。

失敗した場合は再実行の必要があるため、場合によっては悲観ロックを使った方がパフォーマンスが良かったりもします。

その他

Pipeline、MultiでGETを使うケース

PipelineやMultiスコープ内での意思決定の値として使わないケースであれば問題なく使えます。

例えば何度かINCR, SETした後で、最後にGETを実行して最終的な値を取得する、といったケースでは使われます。
Pipelineは各コマンドの結果が配列で返るためです。

まとめ

RedisにおけるPipeline, Multi, Watchが必要となる課題とそれぞれの使いどころを説明しました。