MemcachedとRedis分散ロックスキーム
6354 ワード
分散キャッシュにより、1台のサーバメモリが無限に拡張できないボトルネックを解決できます.分散キャッシュのアプリケーションでは、複数のクライアントが同時に競合する問題が発生します.この場合、分散ロックを使用して、ロックを取得したクライアントに操作権限があります.
MemcachedおよびRedisは一般的な分散キャッシュ構築スキームであり、以下にMemcachedおよびRedis分散ロックに基づく実装方法を列挙する.
Memcached分散ロック
Memcachedはaddコマンドを使用できます.このコマンドはKEYが存在しない場合にのみ追加されるか、処理されません.Memcachedのすべてのコマンドは原子的で、addと同じKEYを発行して、1つだけ成功します.
この原理を利用して,ロックロックロックKEYを定義することができ,addはロックを得ることに成功したと考えられる.また[期限切れタイムアウト]時間を設定し、ダウンタイム後もデッドロックしないことを保証します.
具体的な操作が完了した後、今回の操作がタイムアウトしたかどうかを判断します.タイムアウトした場合はロックは削除されず、タイムアウトしない場合はロックは削除されます.
疑似コード:
Redis分散ロック
Redisにはaddコマンドはありませんが、SETNX(SET if Not eXists)があり、与えられたkeyが既に存在する場合、SETNXは何もしません.設定に成功し、1を返します.設定に失敗し、0を返します.
SETNXコマンドでは有効期限を設定できません.EXPIREコマンドで有効期限を設定する必要があります.
疑似コード:
このようなやり方には、大きな潜在的なリスクがあります.[1]ロックを取得してから[2]を実行して有効期限を設定します.この期間中にダウンタイムが発生した場合、有効期限が設定されていません.Redisのデフォルトキャッシュの期限切れポリシーを押すと、このロックは解放されず、デッドロックが発生します.
このような方法では、ロックのタイムアウト期限切れポリシーを他の方法で実現することは推奨されません.
1:SETNX value値=現在時間+期限切れタイムアウト時間、1を返すとロックが取得され、0を返すとロックが取得されません.回転2.
2:GETはvalueの値を取得します.ロックが期限切れになったかどうかを判断します.タイムアウトした場合は、3を回します.
3:GETSET(所与のkeyの値をvalueとし、keyの旧値を返す)、GETSET value値=現在時間+期限切れタイムアウト時間であり、判定されたvalueがタイムアウトであれば、ロックが得られることを示し、そうでなければロックが得られない.
2から3への同時進行操作では,タイムアウト時間は複数回書き換えられるが,これはあまり影響しない.
疑似コード:
個人的には、このようなやり方は、まだ完璧ではないと思います.
ZooKeeperの分布式ロックについて,次編で検討する.
参照先:
鄭昀,
電子商取引課題V:分散ロック
jeffkit,
Redisによる分散ロック
MemcachedおよびRedisは一般的な分散キャッシュ構築スキームであり、以下にMemcachedおよびRedis分散ロックに基づく実装方法を列挙する.
Memcached分散ロック
Memcachedはaddコマンドを使用できます.このコマンドはKEYが存在しない場合にのみ追加されるか、処理されません.Memcachedのすべてのコマンドは原子的で、addと同じKEYを発行して、1つだけ成功します.
この原理を利用して,ロックロックロックKEYを定義することができ,addはロックを得ることに成功したと考えられる.また[期限切れタイムアウト]時間を設定し、ダウンタイム後もデッドロックしないことを保証します.
具体的な操作が完了した後、今回の操作がタイムアウトしたかどうかを判断します.タイムアウトした場合はロックは削除されず、タイムアウトしない場合はロックは削除されます.
疑似コード:
1 if (mc.Add("LockKey", "Value", expiredtime))
2 {
3 //
4 try
5 {
6 //do business function
7
8 //
9 if (!CheckedTimeOut())
10 {
11 mc.Delete("LockKey");
12 }
13 }
14 catch (Exception e)
15 {
16 mc.Delete("LockKey");
17 }
18
19 }
Redis分散ロック
Redisにはaddコマンドはありませんが、SETNX(SET if Not eXists)があり、与えられたkeyが既に存在する場合、SETNXは何もしません.設定に成功し、1を返します.設定に失敗し、0を返します.
SETNXコマンドでは有効期限を設定できません.EXPIREコマンドで有効期限を設定する必要があります.
疑似コード:
int lockResult = rd.SETNX("LockKey", "Value");
if (lockResult == 1)
{
//[1]
//[2]
rd.EXPIRE("LockKey", expiredtime);
try
{
//do business function
//
if (!CheckedTimeOut())
{
rd.DEL("LockKey");
}
}
catch (Exception e)
{
rd.DEL("LockKey");
}
}
このようなやり方には、大きな潜在的なリスクがあります.[1]ロックを取得してから[2]を実行して有効期限を設定します.この期間中にダウンタイムが発生した場合、有効期限が設定されていません.Redisのデフォルトキャッシュの期限切れポリシーを押すと、このロックは解放されず、デッドロックが発生します.
このような方法では、ロックのタイムアウト期限切れポリシーを他の方法で実現することは推奨されません.
1:SETNX value値=現在時間+期限切れタイムアウト時間、1を返すとロックが取得され、0を返すとロックが取得されません.回転2.
2:GETはvalueの値を取得します.ロックが期限切れになったかどうかを判断します.タイムアウトした場合は、3を回します.
3:GETSET(所与のkeyの値をvalueとし、keyの旧値を返す)、GETSET value値=現在時間+期限切れタイムアウト時間であり、判定されたvalueがタイムアウトであれば、ロックが得られることを示し、そうでなければロックが得られない.
2から3への同時進行操作では,タイムアウト時間は複数回書き換えられるが,これはあまり影響しない.
疑似コード:
string expiredtime = DateTime.Now.AddMinutes(LockTimeoutMinutes).ToString();
int lockResult = rd.SETNX("LockKey", expiredtime);
bool getLock = false;
if (lockResult == 1)
{
//
getLock = true;
}
else
{
string curExpiredtime = rd.GET("LockKey");
//
if (CheckedLockTimeOut(expiredtime))
{
expiredtime = DateTime.Now.AddMinutes(LockTimeoutMinutes).ToString();
string newExpiredTime = GETSET(expiredtime);
if (CheckedLockTimeOut(newExpiredTime))
{
//
getLock = true;
}
}
}
if (getLock)
{
try
{
//do business function
//
if (!CheckedTimeOut())
{
rd.DEL("LockKey");
}
}
catch (Exception e)
{
rd.DEL("LockKey");
}
}
個人的には、このようなやり方は、まだ完璧ではないと思います.
ZooKeeperの分布式ロックについて,次編で検討する.
参照先:
鄭昀,
電子商取引課題V:分散ロック
jeffkit,
Redisによる分散ロック