トランザクションのデモ
Redisのトランザクションは、複数のコマンドリクエストをパッケージ化し、複数のコマンドを一度に順次実行する仕組みを提供します。
トランザクションの実行中、サーバーはトランザクションを中断して他のクライアントのコマンド要求を実行することはありません。
次の図は、Redisトランザクションの実行を示しています:
ご覧のように、トランザクションはMULTIコマンドで始まり、複数のコマンドがトランザクションに入れられ、最後にEXECコマンドでトランザクションをサーバーにコミットして実行します。
トランザクション実装の原則
取引は最初から最後まで次の3つの段階を経て行われます:
- トランザクション開始
- キュー内のコマンド
- トランザクションの実行
トランザクション開始
MULTIコマンドの実行は、トランザクションの開始を意味します。
このコマンドを実行すると、クライアント状態のflags属性がREDIS_MULTI識別子に変わり、クライアントが非トランザクション状態からトランザクション状態に切り替わったことを示します。
キュー内のコマンド
クライアントが非トランザクション状態にある場合、このクライアントから送信されたコマンドはサーバによって直ちに実行されます:
クライアントがトランザクション状態にあるとき、そのクライアントから送信されたコマンドがサーバーで即座に実行されるかどうかは、以下の2つのケースに分かれます:
- クライアントがMULTI、EXEC、WATCH、DISCARDの4つのコマンドのうち1つを送信すると、サーバはそのコマンドを即座に実行します。
- クライアントが上記4つのコマンド以外のコマンドを送信した場合、サーバーはそのコマンドをすぐに実行せず、トランザクションキューに入れ、クライアントにQUEUED応答を返します。
上記のプロセスは以下のフローチャートで表すことができます:
DISCARDコマンドは、トランザクションをキャンセルし、トランザクションブロック内のすべてのコマンドの実行を中断するために使用されます:
次にトランザクション・キューに言及すると、各Redisクライアントは独自のトランザクション状態を持ち、これはクライアント状態のmstatesプロパティに格納されます:
トランザクション・ステータスには、以下のように、1つのトランザクション・キューと1つのキューイングされたコマンド数が含まれます:
トランザクション・キューは、multiCmd型の配列です。配列の各multiCmd構造体は、キューに入れられたコマンドに関する情報を保持します:
- GETコマンド、SETコマンドなどのコマンド実装関数へのポインタ。
- コマンドパラメータ
- パラメータ数
トランザクション・キューは、**先入れ先出し**方式で受信コマンドを保持します。
トランザクションの実行
トランザクション状態にあるクライアントがEXECコマンドを実行すると、サーバはそのクライアントのトランザクション・キューを走査し、キューに保存されているすべてのコマンドを(先入れ先出しで)実行し、コマンドの実行結果をクライアントに一度に返します。
WATCHコマンド実装の原則
WATCHコマンドは、任意の数のデータベース・キーを監視するために使用され、EXECコマンドの実行中に監視対象のキーが変更されたかどうかを検出します。変更された場合、サーバはトランザクションの実行を拒否し、クライアントに空の応答を返します。
まず、クライアント1を開き、WATCHコマンドを実行してキー "name "を監視し、次にトランザクションを開きます:
この時点で、EXECコマンドは実行せず、クライアント2を開き、以下のコマンドを実行して「name」キーの値を変更します:
その後、クライアント1でEXECコマンドを実行すると、クライアント2で「name」キーの値が変更されたため、空の応答が返されます:
では、WATCHコマンドの実装はどのようなものでしょうか?それは以下の3つの方法で分析されます:
- WATCHコマンドによるデータベース・キーの監視
- ウォッチドッグ・メカニズムのトリガー
- 取引が安全かどうかの判断
WATCHコマンドによるデータベース・キーの監視
各Redisデータベースは1つのwatched_keys辞書を保持しており、そのキーはWATCHコマンドによって監視されている特定のデータベースキーであり、その値は対応するデータベースキーを監視しているすべてのクライアントの連鎖リストです。
例えば、クライアント1が "name "というキーを監視し、クライアント2が "age "というキーを監視している場合、watched_keys辞書にはおそらく以下のデータが格納されます:
このとき、クライアント3が以下のWATCHコマンドを実行した場合:
すると、watched_keys辞書に格納されているデータは次のようになります:
ウォッチドッグ・メカニズムのトリガー
WATCHコマンドでウォッチされるキーはwatched_keys辞書に格納されていますが、ウォッチ機構はどのようにトリガーされるのでしょうか?
その答えは、SET、LPUSH、SADDなどのデータベースを変更するすべてのコマンドは、実行後にwatched_keys辞書をチェックし、いずれかのクライアントがコマンドによって変更されたキーを監視している場合、そのキーを監視しているすべてのクライアントでREDIS_DIRTY_CAS識別子がオンになり、そのクライアントのトランザクション・セキュリティが破られたことを示します。がオンになります。
例えば、キー「name」の値が変更された場合、クライアント1とクライアント3のREDIS_DIRTY_CAS識別子がオンになります。
取引が安全かどうかの判断
最後の非常に重要なステップは、サーバーがクライアントからEXECコマンドを受信したときに、このクライアントがREDIS_DIRTY_CAS識別子をオンにしているかどうかに基づいてトランザクションを実行するかどうかを判断することであり、判断のフローチャートを以下に示します:
失敗した取引の例
このトランザクションはコマンド入力エラーのためサーバーに拒否され、トランザクション内のすべてのコマンドは実行されません:
2番目の例をもう一度見てみると、トランザクションがキューに入れられると、存在しないコマンドが表示され、サーバーはトランザクションの実行を拒否します:
3番目の例では、RPUSHコマンドは実行中にエラーを報告しましたが、後続のコマンドは実行され続け、実行されたコマンドは何ら影響を受けませんでした:
この例は、Redisのトランザクションがロールバック機構をサポートしていないことも示しています。
まとめ
Redisのトランザクションは、複数のコマンドをパッケージ化し、それらを単一の順序だった方法で実行するメカニズムを提供します。
これは、複数のコマンドがトランザクションキューにキューイングされ、FIFO順序で実行されるという原則に基づいています。
また、トランザクションは実行中に中断されることはなく、トランザクションキュー内のコマンドがすべて実行された時点で終了します。
黄建宏 Redisの設計と実装




