1.分散ロックは、の定義を理解しました
同時に複数のタスクを防止するために、データの変更や削除を行うときに、同時タスクでは、データは、その後、私たちは、プログラムの同時実行を制限するために、分散ロックを使用する必要があり、混乱を生成します。
Redisのは、他のプロセスがこの領土を使用する際に、Redisの年における領土と宣言しているの本質で達成すべき目標のロックを分散し、プロセスがこの領土を所有して発見されたあきらめるか、待つことを選択しなければなりませんでした。
分散ロックの2.Redis使用
通常命令とだけRedisの中で、クライアントによって占められてもよい(存在しない場合は設定)setnxを使用して領土を宣言しました。最初の使用の完了時に領土を残すためにdelコマンドを呼び出して、最初に務めた基礎を来ります。
>setnx island-1 ll
(integer) 1
>setnx island-1 pp
(integer) 0
>get island-1
"ll"
>del island-1
(integer) 0
>get island-1
(nil)
実装のプロセスは、デルの命令がリリース後に呼び出すことができない異常が生じ、半分の領土を考慮している場合でも、この使用は、いくつかの問題になり、これがデッドロック現象が発生します、領土のその部分が占有されていた、ロック彼はリリースすることができませんでした。
この時点で、また、ロックが5秒後に自動的に解放されることを保証することが可能であっても例外の途中に登場するように、(キー5の有効期限)など5Sとして、有効期限にロックを追加するのが一般的です。
>setnx island-2 ll
ok
>expire island-2 5
>get island-2
"ll"
...5s之后...
>get island-2
(nil)
しかし、あなたはまだプログラムがsetnx命令と突然の停電や人間の操作としてハングアップする命令の間で期限が切れるならば、同じことが、デッドロックを引き起こす可能性があり、このような問題を持っています。根本的な問題はsetnxであり、コマンドが同時に実行されません有効期限が切れます。
一般的な考え方は、解決するためにトランザクションを使用するのではと思うかもしれないが、残念ながらこの方法は、理由は期限切れになり現実的ではありませんsetnxエンクロージャに障害が発生した場合、setnxの実施の結果に依存して、期限切れに実行することができない、との取引の特性のいずれかでありますすべてが実行されたり実行されません。この問題を解決することができるように幸いなことに、redis2.8将来のリリースでは、拡張命令セットのパラメータを追加します。
>set island-2 ll ex 5 nx
ok
>get island-2
"ll"
... 5s之后...
>get island-2
(nil)
前記NX(存在しない場合)、EXすなわち期限切れ
3. Redisの分散ロック延長
3.1タイムアウトの問題
タイムアウト制限を超えてロックし、ロックを解除するので、長い間の実行時間が設定されている場合、それは実行の最初の論理スレッドにつながるので、ロック・タイムアウトは、問題を解決することはできません分散型のRedisは完了していません、他のスレッドがロックにハイジャックされます。これを避けるために、Redisのは、一般的に長い時間のタスクのために使用されていないロックを配布しました。
3.2再入
リエントラントは、同じスレッドでロックがこの機能をサポートしている場合、以前に、ロックは再びロック開催された要求の場合には、ロックが再入可能であることを意味します。Redisのは、再入を達成するために、ロックを分散し、ThreadLocalの変数は、現在ロックを保持するスレッド数を保存するために、クライアントをパッケージ化するための方法を設定する必要があります。次のようにコードのPythonのバージョンは次のとおりです。
import redis
import threading
locks = threading.local()
locks.redis = {}
def key_for(user_id):
return 'account_{}'.format(user_id)
# 加锁
def _lock(client,key):
return bool(client.set(key,True,nx=True,ex=5))
# 解锁
def _unlock(client,key):
client.delete(key)
# 执行加锁 + 计数
def lock(client,user_id):
key = key_for(user_id)
if key in locks.redis:
locks.redis[key] += 1
return True
ok = _lock(client,key)
if not ok:
return False
locks.redis[key] = 1
return True
# 执行解锁 + 计数
def unlock(user_id):
key = key_for(user_id)
if key in locks.redis:
locks.redis[key] -= 1
if locks.redis[key] <= 0:
del locks.redis[key]
return True
return False
client = redis.StrictRedis()
print('lock',lock(client,'ll')) # lock True
print('lock',lock(client,'ll')) # lock True
print('unlock',unlock('ll')) # unlock False 未完全解锁
print('unlock',unlock('ll')) # unlock False
これは、正確なリエントラントロックではありません、あなたはまた、有効期限を追加することができる、などが、コードの複雑さは常に増加しますので、リエントラントロックの使用を推奨していません。
-------------------------------------------------- ---------------------------------------
Redisの深さ「への記事参照冒険:コア原則とアプリケーションの実践「 -著者:銭製品
---------------------------------------- -------------------------------------------------