redisの分散ロックとしての設計

3772 ワード

分散ロックの設計
  • はロックを取得し、if(get(key)は存在しない)set key、val;
  • ロック、del keyを削除します.

  • ロックの取得
    ロックを取得する前に、既にロックがあるか否かを判断する.
    while(1)
      val = redis.get(key)
      if(val != nil )
          wait;
          continue;
      redis.set(key, val)
      break;

    しかし、getとsetの間に他のclient setがkeyに遭遇する可能性があり、複数のclientが同時にロックを取得する.ロックの2つのプロセス、getとsetの原子性をどのように取得するか.使用コマンド:set key value(uuid)NX PX timeout
    NX:keyが存在しない場合に追加する.キーが存在するならnilに戻るOnly set the key if it already exist. XX:keyが存在する場合に追加します.EX/PX:有効期限の単位、EXは秒単位、PXはミリ秒単位を表す.
    なぜ期限切れを設定するのか:クライアントのクラッシュやその他の異常によるロックの解放を回避し、ロックが常に占有される.
    なぜvalueはuuidの一意の値として定義され、異常な場合に誤って削除され、同時に2つのclientが共有ロックを持つことを避けるのか.
    valueのデザイン
    valueのデザインはuuidやタイムスタンプなどが望ましい.ロックの誤削除を避ける.例外の説明-Aクライアントはオブジェクトロックを取得したが、いくつかの理由でブロックされたため、タイムリーにロックを解除できなかった-期限が切れたため、Redisのロックオブジェクトが削除された-Bクライアントはロック操作に成功した-Aクライアントはこの時にブロック操作が完了し、keyを削除してロックを解放した-Cクライアントは成功した-これはB、Cがロックを取得した.分散ロックの無効化
    このような状況を回避するために、valueの設計が一意であることを保証するには、ロックを取得し、valueが予想されるクライアントに合致している場合にのみ削除できます.
    リリースロック
    releaseロックには2つのステップがあります:1.get,valueが期待を満たすかどうかを検証する.delロック
    このステップはどのように原子性を保証しますか?redisのWATCHコマンドを使用する楽観ロック方式を使用する.WATCHコマンド自体は楽観的なロックであり、EXECコマンドが実行される前に一定数のkeyを監視し、EXEC実行時にこれらのkeyが変更されたかどうかを確認し、もしそうであればサーバはトランザクションの実行を拒否することができます.
    redis.watch(lock)
    val = redis.get(lock) 
    if val ==    
      redis.multi
      redis.del(key)
      redis.exec

    悲劇、上のwatchのメカニズムは依然としてworkできません.なぜならwatchはmultiと組み合わせて使用する.watchは実際にトランザクションを開いてから有効になります.どのように実現しますか?luaスクリプト、RedisはLuaスクリプトを全体として実行します.Redisは単一スレッドであるため、スクリプト実行中に他のスクリプトやコマンドは実行を挿入できません.この特性はトランザクションの原子性に合致します.
    コマンドプロトタイプ:
    EVAL script numkeys key[key ...] arg [arg...]

    次のluaスクリプトを参照してください.
    local key = KEYS[1]  
    local val = redis.call("GET", key) 
    if val == ARGV[1] 
      then 
        local ret = redis.call("del", key) 
        return ret
    end

    以下のコマンドチャネルredis-cliに直接貼り付けて実行すると、理解が深まります.
    set lock 1234
    eval 'local key = KEYS[1]  local val = redis.call("GET", key) \
    if val == ARGV[1] then local ret = redis.call("del", key) \
    return ret end' 1 "lock" "1234"
    get lock

    その他の補足