redisの分散ロックとしての設計
3772 ワード
分散ロックの設計はロックを取得し、if(get(key)は存在しない)set key、val; ロック、del keyを削除します.
ロックの取得
ロックを取得する前に、既にロックがあるか否かを判断する.
しかし、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が変更されたかどうかを確認し、もしそうであればサーバはトランザクションの実行を拒否することができます.
悲劇、上のwatchのメカニズムは依然としてworkできません.なぜならwatchはmultiと組み合わせて使用する.watchは実際にトランザクションを開いてから有効になります.どのように実現しますか?luaスクリプト、RedisはLuaスクリプトを全体として実行します.Redisは単一スレッドであるため、スクリプト実行中に他のスクリプトやコマンドは実行を挿入できません.この特性はトランザクションの原子性に合致します.
コマンドプロトタイプ:
次のluaスクリプトを参照してください.
以下のコマンドチャネルredis-cliに直接貼り付けて実行すると、理解が深まります.
その他の補足
ロックの取得
ロックを取得する前に、既にロックがあるか否かを判断する.
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
その他の補足