この記事では、主に分散ロックとは何かを紹介する、redis に関する関連知識を提供します。 Redis は分散ロックをどのように実装しますか?どのような条件を満たす必要がありますか?以下を見てみましょう。困っている友達のお役に立てれば幸いです。
分散ロック: 分散システムまたはクラスター モードの複数のプロセスに表示され、相互に排他的なロック。
分散ロックが満たすべき条件:
2. Redis に基づく分散ロックの実装
#分散ロックを実装する場合は、次の 2 つの基本メソッドを実装する必要があります:
SET resource_name my_random_value NX PX 30000
バージョン 1
1. ILock インターフェイスの定義 public interface ILock extends AutoCloseable {
/**
* 尝试获取锁
*
* @param timeoutSec 锁持有的超时时间,过期后自动释放
* @return true代表获取锁成功;false代表获取锁失败
*/
boolean tryLock(long timeoutSec);
/**
* 释放锁
* @return
*/
void unLock();
}
public class SimpleRedisLock {
private final StringRedisTemplate stringRedisTemplate;
private final String name;
public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
private static final String KEY_PREFIX = "lock:";
@Override
public boolean tryLock(long timeoutSec) {
//获取线程标识
String threadId = Thread.currentThread().getId();
//获取锁
Boolean success = stringRedisTemplate.opsForValue()
.setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success);
}
@Override
public void unLock() {
//通过del删除锁
stringRedisTemplate.delete(KEY_PREFIX + name);
}
@Override
public void close() {
unLock();
}
}
ロックの誤削除の問題
ロックを保持しているスレッド 1 はロック内でブロックされています。このとき、ロックはタイムアウト後に自動的に解放されます。このとき、スレッド 2 はロックの取得を試みます。その後、スレッド 2 の実行中に、ロックを保持しています。ロックを実行すると、スレッド 1 が反応して実行を継続します。ロック ロジックを削除すると、スレッド 2 に属するはずのロックが削除されます。これは、ロックを誤って削除した場合です。
解決策:ロックを保存するときは、自分のスレッドの識別子を入力し、ロックを削除するときは、現在のロックの識別子が自分のスレッドの識別子であるかどうかを確認します。保存されています。入力されている場合は削除され、入力されていない場合は削除されません。
バージョン 2: 偶発的なロック削除の問題を解決する
public class SimpleRedisLock { private final StringRedisTemplate stringRedisTemplate; private final String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) { this.stringRedisTemplate = stringRedisTemplate; this.name = name; } private static final String KEY_PREFIX = "lock:"; private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-"; @Override public boolean tryLock(long timeoutSec) { //获取线程标识 String threadId = ID_PREFIX + Thread.currentThread().getId(); //获取锁 Boolean success = stringRedisTemplate.opsForValue() .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } @Override public void unLock() { // 获取线程标示 String threadId = ID_PREFIX + Thread.currentThread().getId(); // 获取锁中的标示 String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name); // 判断标示是否一致 if(threadId.equals(id)) { // 释放锁 stringRedisTemplate.delete(KEY_PREFIX + name); } } @Override public void close() { unLock(); } }
上記の解放ロックコードには依然として誤ってロックを削除してしまう問題があり、スレッド 1 がロック内のスレッド ID を取得し、その ID に基づいて自分のロックであると判断すると、ロックが期限切れになると自動的にロックが解放されます。スレッド 2 がロックを取得しようとしてロックを取得しますが、スレッド 1 がロックを解放する操作を実行しているときに、スレッド 2 が保持しているロックが誤って削除されてしまいます。
その理由は、Java コードによって実装されるロック解放プロセスがアトミックな操作ではなく、スレッドの安全性の問題があるためです。
解決策:Redis には Lua スクリプト機能があり、複数の Redis コマンドを 1 つのスクリプトに記述することで、複数のコマンドの実行をアトミックに実行できます。
バージョン 3: Lua スクリプトを呼び出して分散ロックを変換する
public class SimpleRedisLock implements ILock { private final StringRedisTemplate stringRedisTemplate; private final String name; public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) { this.stringRedisTemplate = stringRedisTemplate; this.name = name; } private static final String KEY_PREFIX = "lock:"; private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-"; @Override public boolean tryLock(long timeoutSec) { //获取线程标识 String threadId = ID_PREFIX + Thread.currentThread().getId(); //获取锁 Boolean success = stringRedisTemplate.opsForValue() .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS); return Boolean.TRUE.equals(success); } @Override public void unLock() { String script = "if redis.call("get",KEYS[1]) == ARGV[1] then\n" + " return redis.call("del",KEYS[1])\n" + "else\n" + " return 0\n" + "end"; //通过执行lua脚本实现锁删除,可以校验随机值 RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean.class); stringRedisTemplate.execute(redisScript, Collections.singletonList(KEY_PREFIX + name), ID_PREFIX + Thread.currentThread().getId()); } @Override public void close() { unLock(); } }
以上が分散ロックの原理と、Redis が分散ロックを実装する方法について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。