Redisトランザクションとwatch


redisのトランザクション
厳密には、redisのトランザクションは、mysqlなどの従来のデータベースのトランザクションとは異なります.
redisのトランザクション定義
Redisのトランザクション(transaction)は、コマンドのセットです.
トランザクションはコマンドと同様にRedisの最小実行単位であり、1つのトランザクションのコマンドはすべて実行されるか、実行されません.トランザクションの原理は、トランザクションに属するコマンドをRedisに送信してから、Redisにコマンドを順次実行させることです.
Redisは、トランザクション内のすべてのコマンドが実行されるか、実行されないかを保証します.EXECコマンドを送信する前にクライアントが断線した場合、Redisはトランザクションキューを空にし、トランザクション内のすべてのコマンドは実行されません.クライアントがEXECコマンドを送信すると、すべてのコマンドが実行され、その後クライアントが断線しても大丈夫です.Redisには実行するすべてのコマンドが記録されているからです.
また、Redisのトランザクションは、他のコマンドに挿入されずに1つのトランザクション内のコマンドが順次実行されることを保証します.クライアントAはいくつかのコマンドを実行する必要があると考え、クライアントBはトランザクションを使用しない場合、クライアントBのコマンドがクライアントAのいくつかのコマンドに挿入されて実行されるコマンドを送信します.このような状況が発生したくない場合は、トランザクションも使用できます.
 
redisトランザクション持続性
トランザクションは、Redisコマンドのセットをキューでラップしたものにすぎません.追加の永続性機能はありません.したがって、トランザクションの永続性は、Redisが使用する永続化モードによって決まります.
  • 単純なメモリモードでは、トランザクションは永続的ではないに違いありません.
  • RDBモードでは、サーバがトランザクションの実行後、RDBファイルの更新前の時間に失敗する可能性があるため、RDBモードでのRedisトランザクションも永続的ではない.
  • AOFの「常にSYNC」モードでは、トランザクションの各コマンドが正常に実行されると、fsyncまたはfdatasyncが呼び出されてトランザクションデータがAOFファイルに書き込まれます.ただし、この保存はバックグラウンドスレッドによって行われ、プライマリスレッドは保存に成功するまでブロックされないため、コマンドの実行に成功してからデータがハードディスクに保存されるまでの間隔が非常に小さいため、このモードでのトランザクションも永続的ではありません.
  • の他のAOFモードも「常にSYNC」モードと類似しているため、それらはいずれも永続的ではない.

  • redis分離性と一貫性
    redisトランザクションは、実行中に他のコマンドを処理するのではなく、すべてのコマンドが実行された後、他のコマンド(隔離性を満たす)を処理します.redisトランザクションは、実行中にエラーが発生したり、プロセスが終了したりして、データの一貫性を保証します.(詳細は参考資料1参照)
    トランザクションの適用
    銀行振替中にAがBに送金するなど、事務の応用は非常に一般的で、まずシステムがAの口座からお金を引き出し、それからBの口座に相応の金額を増やす.この2つのステップは同じ事務に属しなければならない.全部実行するか、全部実行しないか.そうしないと、最初のステップだけを実行して、お金が空で消えてしまうのは明らかに受け入れられない.
    従来の事務とは違う
    従来のmysqlトランザクションとは異なり、追加操作に失敗しても、このコマンドセットでステータス全体を操作前にロールバックすることはできません.
    トランザクションのエラー処理
    トランザクション内のコマンドの実行エラーが発生した場合、Redisはどのように処理しますか?この質問に答えるには、まず、コマンドの実行エラーの原因を知る必要があります.
    構文エラー
    構文エラーは、コマンドが存在しないか、コマンド・パラメータの個数が間違っていることを意味します.たとえば、次のようにします.
    redis>MULTI
    OK
    redis>SET key value
    QUEUED
    redis>SET key
    (error)ERR wrong number of arguments for 'set' command
    redis> errorCOMMAND key
    (error) ERR unknown command 'errorCOMMAND'
    redis> EXEC
    (error) EXECABORT Transaction discarded because of previous errors.

    MULTIコマンドの後に3つのコマンドが実行されました.1つは正しいコマンドで、トランザクションキューに正常に追加されました.残りの2つのコマンドには文法エラーがあります.一方、1つのコマンドに文法エラーがあれば、EXECコマンドを実行するとRedisは直接エラーに戻り、文法の正しいコマンドも実行されません.
    ここで注意したいのは、Redis 2.6.5以前のバージョンでは、構文エラーのあるコマンドは無視され、トランザクション内の他の構文が正しいコマンドが実行されます.この例では、SET key valueが実行され、EXECコマンドは結果:1)OKを返します.
    実行エラー
    実行エラーとは、ハッシュ・タイプのコマンドを使用してコレクション・タイプのキーを操作するなど、コマンド実行時に発生するエラーを指します.このエラーは、実際に実行するまではRedisでは発見できません.したがって、トランザクションではこのようなコマンドはRedisに受け入れられて実行されます.トランザクション内のコマンドの1つに実行エラーが発生した場合、トランザクション内の他のコマンドは実行され続けます.(エラーコマンドの後のコマンドを含む)例は次のとおりです.
    redis>MULTI
    OK
    redis>SET key 1
    QUEUED
    redis>SADD key 2
    QUEUED
    redis>SET key 3
    QUEUED
    redis>EXEC
    1) OK
    2) (error) ERR Operation against a key holding the wrong kind of value
    3) OK
    redis>GET key
    "3"

    SADD key 2でエラーが発生しましたが、SET key 3は実行されています.
    Redisのトランザクションには、データベース・トランザクションが提供するロールバック機能はありません.このため、開発者は、トランザクションの実行エラー後に残りの屋台を自分で片付けなければなりません.(トランザクション実行前の状態にデータベースを復元するなど、ここでは一般的にログを記録してビジネス補償する方式で処理していますが、一般的にはredisで行う操作にはこのような強い一貫性の要求があるべきではなく、このような需要は理不尽な設計だと考えています).
    Watchコマンド
    redisがincrコマンドに基づいて整数型数値の原子増分を操作することを提供していることを知っているかもしれませんが、redisにこのincrコマンドがなければ、私たちはどのようにこのincrの操作を実現すればいいと仮定しますか?
    では、私たちの下の正主watchが登場します.
    watchコマンドの使用方法
    通常、整形数値を修正するには、次のようにします(擬似コード実装):
          val = GET mykey
          val = val + 1
          SET mykey $val

    しかし、上記のコードでは、正常なincr(原子増幅動作)が2つの部分に分かれているため、マルチスレッド(分布式)環境では、この動作が原子性を持たない可能性があるという問題が発生します.
    Javaのjucパッケージを研究した人はcasを知っているはずですが、redisもwatchコマンドを利用して実現するメカニズムを提供しています.
    watchコマンド説明
    WATCHコマンドは、1つまたは複数のキーを監視することができ、1つのキーが変更(または削除)されると、その後のトランザクションは実行されません.EXECコマンド(トランザクション内のコマンドはEXECの後に実行されるので、MULTIコマンドの後にWATCH監視のキー値を変更できます)まで監視します.
    watchによるincrの実現
    具体的な方法は以下の通りです.
          WATCH mykey
          val = GET mykey
          val = val + 1
          MULTI
          SET mykey $val
          EXEC

    これまでのコードとは異なり、新しいコードはmykeyの値を取得する前にWATCHコマンドでキーを監視し、その後setコマンドをトランザクションに囲みます.これにより、各接続がEXECを実行する前に、現在接続で取得したmykeyの値が接続されているクライアントによって変更されると、現在接続されているEXECコマンドが失敗することを効果的に保証できます.これにより、呼び出し元戻り値を判断するとvalが再設定に成功したかどうかがわかります.
    注意点
    WATCHコマンドの役割は、監視されているキー値が変更された後にトランザクションの実行がブロックされるだけで、他のクライアントがこのキー値を変更しないことを保証することはできませんので、一般的にEXECの実行に失敗した後に関数全体を再実行する必要があります.
    EXECコマンドを実行すると、すべてのキーのモニタリングがキャンセルされます.トランザクションのコマンドを実行したくない場合は、UNWATCHコマンドを使用してモニタリングをキャンセルすることもできます.
    hsetNX関数の実装
    我々が実装したhsetNXという機能は,フィールドが存在する場合にのみ付与される.
    競合条件を回避するために、watchとトランザクションを使用して、この機能(偽コード)を完了します.
        WATCH key  
        isFieldExists = HEXISTS key, field  
        if isFieldExists is 1  
        MULTI  
        HSET key, field, value  
        EXEC  
        else  
        UNWATCH  
        return isFieldExists

     
    コードでは、付与するフィールドが存在するかどうかを判断し、フィールドが存在しない場合はトランザクションのコマンドは実行されませんが、UNWATCHコマンドを使用して次のトランザクションの実行に影響を与えないようにする必要があります.
     
    ネットワーク特性の最適化
    複数のコマンドをredisサーバに一括送信して実行し、ネットワークのインタラクションを低減し、パフォーマンスを最適化し、可能なソリューションを提供します.
  • すべてのget/set操作に対して、既存のmget/mset命令を使用することができる;
  • 様々なタイプの更新操作についてluaスクリプトを使用してコマンドをパッケージ化し、サーバ側に一度に送信することができる.