Redisトランザクションの実装


詳細
RedisトランザクションはMULTI,EXEC,WATCHなどのコマンドにより実現される.このセクションでは、これらのコマンドの実装の詳細について説明します.
Redisトランザクションの開始から終了までには、通常、次の3つのフェーズがあります.
1)取引が開始されます.MULTIコマンドの実行は、トランザクションの開始を示します.このコマンドを実行するクライアントは、非トランザクション状態からトランザクション状態に切り替えられます.この切り替えは、クライアント状態のflagsプロパティのREDIS_を開くことによって行われます.MULTI IDで完了しました.
2)入隊を命じる.クライアントがトランザクション状態に切り替わると、クライアントから送信されたコマンドがEXEC、DISCARD、WATCH、MULTIの4つのコマンドのうちの1つである場合、サーバはすぐにこのコマンドを実行します.そうでない場合、サーバはトランザクション・キューに格納し、クライアントにQUEUEDの返信を返します.
3)取引の実行.トランザクション状態のクライアントがサーバにEXECコマンドを送信すると、サーバはそのクライアントトランザクションキュー内のすべてのコマンドを実行し、最後に実行結果を順番にクライアントに返します.また、クライアントのREDIS_もクリアします.MULTI IDは、トランザクションキューデータを空にしながら非トランザクション状態に戻します.
       
トランザクション・キュー
各Redisクライアントは自分のトランザクション状態をredisClientに保存する.mstateプロパティ:

typedef struct redisClient{
    multiState mstate;               // MULTI/EXEC state
    /* other fields ...*/
} redisClient;

typedef struct multiState{
    multiCmd *commands;              //     ,FIFO   
    int count;                       //        
} multiState;

typedef struct multiCmd{
    robj **argv;                     //   
    int argc;                        //     ,     
    struct redisCommand *cmd;        //     
} multiCmd;

トランザクション・キュー内の各multiCmd構造には、コマンド実装関数へのポインタ、コマンドのパラメータ、およびパラメータの数など、エンキューされたコマンドに関する情報が保存されます.
       
WATCHコマンドの実装
WATCHコマンドは、トランザクションの実行前に任意の数のデータベース・キーを監視し、トランザクションの実行時に監視されているキーの少なくとも1つが変更されたかどうかを確認する楽観的なロックです.そうであれば、サーバはトランザクションの実行を拒否し、次のコード・クリップに示すように、トランザクションの実行に失敗した空の返信をクライアントに返します.

redis> WATCH "name"
OK
redis> SET "name" "john"      #              name  
OK
redis> MULTI
OK
redis> SET "name" "peter"
QUEUED
redis> EXEC
(nil)
redis> GET "name"
"john"

各Redisデータベースにはwatched_が保存されています.keys辞書では、WATCHコマンドによって監視されるデータベース・キーであり、値はデータベース・キーを監視するすべてのクライアント・チェーン・テーブルを記録します.この辞書により、サーバは、どのデータベース・キーが監視されているか、および対応する監視クライアントを明確に知ることができる.

typedef struct redisDb{
    dict *watched_keys;        //     WATCH       
    /* other fields ...*/
} redisDb;

データベースを変更するすべてのコマンド(SET、LPUSHなど)は、実行後にwatched_をチェックします.keys辞書では、変更したばかりのデータベース・キーを監視しているクライアントが見つかった場合、そのクライアントのREDIS_が開きます.DIRTY_CAS IDは、クライアントのトランザクションセキュリティが破壊されたことを示します.サーバがクライアントからEXECコマンドを受信すると、クライアントがREDIS_を開いているかどうかに応じてDIRTY_CAS IDは、トランザクションを実行するかどうかを決定します.
       
RedisトランザクションのACIDの性質
従来のリレーショナル・データベースでは、トランザクション機能の信頼性とセキュリティをACIDの性質で検証することがよくあります.Redisでは、トランザクションは常に原子間性(Atomicity)、コンシステンシ(Consistency)、および独立性(Isolation)を有し、Redisが特定の永続化モードで動作する場合、トランザクションも耐久性(Durability)を有する.
Redisのトランザクションと従来のリレーショナル・データベース・トランザクションの最大の違いは、
Redisはトランザクションロールバックメカニズムをサポートしていません.トランザクションキュー内のコマンドが実行中にエラーが発生した場合でも、すべてのコマンドが実行されるまでトランザクション全体が実行されます.
トランザクションに一貫性があるとは、データベースがトランザクションを実行する前に一貫性がある場合、トランザクションが実行された後も、成功したかどうかにかかわらず、データベースは一貫性がある必要があります.ここでの「一貫性」は、データベース自体の定義と要件に合致し、不正または無効なエラーデータが含まれていないことを意味します.
Redisは慎重なエラー検出と簡単な設計によってトランザクションの一貫性を保証します.Redisトランザクションでエラーが発生する可能性のある場所は主に以下の3つです.
1)エンキューエラー.トランザクションがエンキューコマンド中にコマンドが存在しない場合、またはコマンドのフォーマットが正しくない場合、最終的にRedisはトランザクションの実行を拒否します(この場合、Redis 2.6.5以前のバージョンではトランザクションキュー内の正しいコマンドが実行されます).
2)実行エラー.トランザクションの実行中に発生するエラーは、エンキュー時に発見できないエラーです(たとえば、あるタイプのキーを別のタイプのキーとして操作します).これらのエラーは、コマンドの実際の実行時にのみトリガーされます.ただし、上記のように、トランザクションが実行中にエラーが発生しても、サーバはトランザクションの実行を中断することなく、残りのコマンドを実行し続けます.
3)サーバーが停止する.Redisサーバがトランザクションの実行中にダウンタイムした場合、サーバが永続化モードを使用しているかどうかにかかわらず、データベースの一貫性には影響しません.サーバが永続化されていないメモリ・モードで実行されている場合、再起動後のデータベースは空白であり、空白のデータベースは常に一致します.逆に、サーバがRDBまたはAOFモードで動作している場合、サーバの再起動時に既存のRDBファイルまたはAOFファイルに基づいてデータを復元し、データベースを一貫した状態に復元することができる.使用可能なRDBファイルまたはAOFファイルが見つからない場合、再起動後のデータベースは空白になるため、一貫しています.
トランザクションの独立性とは、データベース内に複数のトランザクションが同時に実行されても、互いに影響を及ぼさず、同時実行されたトランザクションとシリアル実行されたトランザクションの結果が完全に同じであることを意味します.Redisは単一スレッドを使用してトランザクションを実行し、サーバはトランザクションの実行中に中断されないことを保証します.したがって、Redisのトランザクションは常にシリアルで実行され、自然に常に独立性があります.
トランザクションの耐久性とは、1つのトランザクションの実行が完了すると、その結果がハードディスクなどの永続的なストレージメディアに保存され、サーバがトランザクションの実行が完了した後にダウンタイムしても、トランザクションの実行結果が失われないことを意味します.Redisのトランザクションは、Redisコマンドのセットをキューで簡単に包むだけであり、トランザクションに追加の永続化機能を提供していないため、Redisトランザクションの耐久性は、Redisが使用する永続化モードによって決定されます.ただし、永続化モードを使用しても、AOF永続化モードでサーバが実行し、appendfsyncオプションの値がalwaysの場合にのみ、Redisトランザクションが耐久性を有します.この場合、プログラムは常にコマンドを実行した後に同期(sync)関数を呼び出すので、コマンドデータをハードディスク(HDD)に実際に保存します(ただし、no-appendfsync-on-rewriteコンフィギュレーションオプションをオンにすると、BGSAVEまたはBGREWRITEAOFコマンドを実行している間に、サーバは一時的にAOFファイルの同期を停止し、I/Oのブロックを最小限に抑えることができます.これにより、トランザクションの結果が失われ、耐久性が低下する可能性があります).一方、サーバがRDB永続化モードで実行されたり、AOF永続化モードで実行されたりしますが、appendfsyncオプションの値がalwaysではない場合、サーバは特定の保存条件が満たされた後にBGSAVEコマンドを実行したり、同期を実行したりする必要があります.これらは、トランザクションデータが最初の時間にハードディスクに保存されることを保証することはできません.そのため、トランザクションデータが失われる可能性があり、耐久性がありません.しかし、どのモードで実行しても、1つのトランザクションの最後(つまりEXECコマンドを実行する前)にSAVEコマンドを加えると、トランザクションの耐久性が保証されますが、効率が低すぎるため、あまり実用性がありません.
参考書:
1、『Redis設計と実現』第19章——事務.