ディレクトリ
Redisのは、ロック進化の歴史を分散しました
過去2年間のマイクロサービスは、分散ロック、データの一貫性は、に焦点を当て、問題を解決するために必要であったが、分散環境では、分散環境で展開より人気、より多くのアプリケーションになることそれは、広く使用されている技術、より広範な分散ロックのRedisの使用に基づいてRedisの、飼育係、一般的に分散実装となっています。
しかし、いくつかの緩い領域がある各リアライズためのロックを分散適切に分散ロックを使用しない場合でも、コードを含め、障害のある実装が存在し得るのRedisの各バージョンを介してネットワークとワークで見深刻な生産の障害が発生することがあり、本論文では、様々な分散ロックは、現在、その欠点を経験して並べ替えを行い、どのように右のRedisの分散ロック・弾力のアドバイスを選択することです。
Redisの各バージョンには、ロック分散します
- V1.0
tryLock(){
SETNX Key 1
EXPIRE Key Seconds
}
release(){
DELETE Key
}
このバージョンでは、最もシンプルなバージョンがリリースされていないことができ、最初のサービスの再起動の適用を避けるか、異常なロックを引き起こすために、操作のロックの有効期限を追加するために発生した高周波のバージョンである必要があり、ロックが解除されていないません。状況。
このアプローチの問題点は、改善されたスキームはLuaのスクリプト(付属SETNXをし、2つのコマンドをEXPIRE)を使用することで、あなたはリクエストのRedisを送信するたびに、アプリケーション例外または再起動した場合、最初のコマンドを実行した後、ロックが期限切れにならないということですが、 Redisのが唯一のコマンドを実行したりクラッシュがマスタースイッチから発生した場合は、まだロックが最終的に解放されていないにつながる、時間が満了していないがあるでしょう。
それは誤用のロックは、この問題は、その後のV3.0のリリースで解決される予定ですので、もう一つの問題は、リリース分散ロックの過程で多くの学生が、関係なく、買収が成功したかどうかのロックは、ロックがついにリリースされているということです。
ロックの問題を解決するには、に基づいて解除できないGETSETの達成するためのコマンド
- V1.1ベースGETSET
tryLock(){
NewExpireTime=CurrentTimestamp+ExpireSeconds
if(SETNX Key NewExpireTime Seconds){
oldExpireTime = GET(Key)
if( oldExpireTime < CurrentTimestamp){
NewExpireTime=CurrentTimestamp+ExpireSeconds
CurrentExpireTime=GETSET(Key,NewExpireTime)
if(CurrentExpireTime == oldExpireTime){
return 1;
}else{
return 0;
}
}
}
}
release(){
DELETE key
}
アイデア:
-
SETNX(キー、ExpireTime)ロックを取得
-
それはロックの取得に失敗した場合、ロックの期限が切れているかどうかをGET(キー)のタイムスタンプチェックによって返さ
-
NewExpireTimeとしてGETSET(キー、ExpireTime)修正値
-
GETによって返された値に等しいがロック成功を取得するために考えられている場合は、古い値のGETSETリターンをチェック
注:このバージョンではなく、タイムスタンプ値が満了値によって判断するコマンドを期限切れに削除しました
問題:
1. 在锁竞争较高的情况下,会出现Value不断被覆盖,但是没有一个Client获取到锁
2. 在获取锁的过程中不断的修改原有锁的数据,设想一种场景C1,C2竞争锁,C1获取到了锁,C2锁执行了GETSET操作修改了C1锁的过期时间,如果C1没有正确释放锁,锁的过期时间被延长,其它Client需要等待更久的时间
- V2.0ベースSETNX
tryLock(){ SETNX Key 1 Seconds}release(){ DELETE Key}
Redisの2.6.12バージョンSETNX増の有効期限パラメータの後、この2つのコマンドは、アトミックの問題を保証することはできません解決するでしょう。しかし、次の場面を想像してみてください。
1. GCは、タスクの実行の原因を待つか、不明するために成功し、その後、C1にロックを取得するためにC1が長すぎる、ロック故障前の最後はロックC1の解放を開始しませんでしたされ
2. C1ロック、及び開始にC2タイムアウトロックを取得した後、C1及びC2において、この時間は、同時にによる繰り返し引き起こさ不明で行われるデータの矛盾に実行されます
最初が終了した場合、それはこの時点でロックC2、C3を解放する3. C1は、ロックを取得するために別のプロセスにつながる可能性
実質的にフローチャート
問題:
1\. 由于C1的停顿导致C1 和C2同都获得了锁并且同时在执行,在业务实现间接要求必须保证幂等性
2\. C1释放了不属于C1的锁
- V3.0
tryLock(){
SETNX Key UnixTimestamp Seconds
}
release(){
EVAL(
//LuaScript
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
)
}
タイムスタンプを指定して、このプログラムの値、およびロックが値の値がロックを取得するかどうかをチェックするときにC2の質問に保持されたロックの解除に言及C1 V2.0バージョンを回避するために、ロックを解除し、またリリースでロックが複数のRedisの操作を伴うため、及びその回避の同時実行の問題にLuaのスクリプトを使用して、アカウントに同時問題の確認と設定モデルを取ります。
問題:
このようグラブのような非常に高い同時実行のシナリオ、赤い封筒のシーンで場合は、物理クロックに他の原因質問、分散環境ではできません保証の一貫性を繰り返し、そこUnixTimestampが質問を繰り返すかもしれませんが、まれに遭遇するUnixTimestampがあるかもしれません。
- V3.1
tryLock(){
SET Key UniqId Seconds
}
release(){
EVAL(
//LuaScript
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
)
}
Redisの後2.6.12 SETはまた、おそらく削除するためにされた文書の正式版の背中を思い出させる、SETNXコマンドに相当NXパラメータを、提供SETNX、 SETEX、 PSETEXをし、SETコマンドでそれを置き換える、別の最適化は、自動インクリメントのみUniqIdを使用することです代わりに、クロックタイムスタンプのような問題点のV3.0を避けるために。
このプログラムは、最適なプログラム分散ロックですが、問題はまだRedisのクラスタ環境に存在する場合:
非同期データ同期Redisのクラスタため、データ同期ロックした後に取得された未完成マスターノードのクラッシュ状況を想定し、ロックがまだ新しいマスターノード、この場合に取得することができる、複数のクライアントが同時にマスターノードにロック取得
ロック分散型のRedis:Redlock
V3.1のバージョンは、シーン内の唯一の単一のインスタンスは、分散ロックのRedisを実装する方法で、安全です外国人専門家が激しい議論を配布された、antirezはで、分散ロックアルゴリズムRedlockを提案distlockトピックの下にあることRedlockの詳細な説明を参照して、以下は中国の説明Redlockアルゴリズム(参照)であります
N個の独立したノードのRedisがあると仮定
-
(ミリ秒単位)現在の時刻を取得します。
-
順次順次N Redisのノードにロックを取得するための操作を行います。ランダムな文字列を含む_my_random_value_同じRedisの単一のノード、フロントのロックを取得するために、この取得処理動作は、また、有効期限(例えば_PX 30000_、すなわち有効時間ロック)を含みます。アルゴリズムはまた、タイムアウト(時間切れ)を取得するロックの操作を実行し続けることができたときにRedisのノードが利用できないことを確認するためには、ロック(数十ミリ秒)の有効時間よりもはるかに小さいです。Redisの後にロックRedisのノードの障害を取得するには、クライアントはすぐに次のノードを試してみてください。ここで障害が、別のクライアントによって保持されたRedisのノードが利用できないような故障の任意のタイプ、またはRedisのノードのロックを含むべきである(注:Redlock説明Redisのは、ノードがここで使用できない場合を述べたが、それはまた)他の障害を含むべきです。
-
どのくらいの時間の合計消費のロックを取得するプロセス全体の計算は、現在時刻と記録の第一工程の時間を減算することによって計算されます。クライアント・ノードが正常からRedisのを取得した場合、ほとんど(> = N / 2 + 1)ロック、ロック取得時間とロックより有効な合計消費時間(ロック有効時間)、次に時間に、クライアントは、最終的に得たこと成功したロック、そうでない場合、最終的に障害がロックを取得すること。
-
最終的な取得ロックが成功した場合、ロックの有効時間は、最初のロックの有効時間マイナス算出ロックステップ3を取得するために消費される時間に等しい、再計算されなければなりません。
-
最終的ロック獲得に失敗した場合は(おそらく取得時のためには、以下のRedisノードロックN / 2 + 1の数よりも、あるいは全体のロック取得プロセスが初期有効ロックを超える時間を消費し)、その後、クライアントはすぐにすべて通知しなければなりませんロックのRedisのノードを開始レリーズ操作(すなわち、RedisのLuaのスクリプトは、前述)。
-
ロックを解除:ロック解除は、ノードのRedisのすべてのアクションを開始
しかし、アルゴリズムの提案のためのマーティンKleppmann 質問、提言は、(トークンの操作を検証する必要があるリソースごとに)トークンのメカニズムをフェンシング基づいている必要があります
1. Redlock在系统模型上尤其是在分布式时钟一致性问题上提出了假设,实际场景下存在时钟不一致和时钟跳跃问题,而Redlock恰恰是基于timing的分布式锁 2. 另外Redlock由于是基于自动过期机制,依然没有解决长时间的gc pause等问题带来的锁自动失效,从而带来的安全性问题。
その後antirezとに応えるマーティンKleppmann合理与え、疑問の問題に対処する方法について複数のクライアントリソースへの同時アクセスにつながる場合に発生した場合のメカニズムだけでなく、実際のシーンのポーズを期限切れ。
問題Redlockについては、最終的にはRedisの分散セキュリティロックに基づいて、それは中国の詳細な説明を与える、と提案したアルゴリズム分析Redlockの問題。
概要
Redisの単一インスタンス分散ロックかどうかにBASED SETNXバージョンまたは分散ロックRedlockは、特性を確保することを目的としています
1\. 安全性:在同一时间不允许多个Client同时持有锁 2\. 活性 死锁:锁最终应该能够被释放,即使Client端crash或者出现网络分区(通常基于超时机制) 容错性:只要超过半数Redis节点可用,锁都能被正确获取和释放
だから、プロセスにおける分散ロックの開発または使用中に予期しない結果が発生を避けるために、安全性と活動を確保します。
また、分散ロックの各バージョンいくつかの問題があり、我々はロックでの実用化のための右のロックロックシーンを選択する必要があり、ロックが使用シナリオには、通常、次のとおりです。
効率(効率):1つのクライアントのみの操作を完了するためには、繰り返し行うことなく、このことは、ロックにアクティブされやすく、唯一の必要性から分散ロックです。
正当性(正確性):ロックまたは同じリソースを保持しながら、クライアントは、より厳格な相互に排他的な確保するため、同時操作を許可しない、ためのコードのビジネスにしながら、このシナリオの下で選択し、使用ロックでより厳密にする必要がありますできるだけ冪等として
Redisの分散ロックの実現のアドレスには多くの課題がありますが、我々はこれらの問題を認識し、正しい場合、合理的な選択と仕事上の分散ロックの正しい使用をRedisの分散ロックを実装する方法を知っておく必要があります。