redis分布式ロック原理解析の実現
21435 ワード
文書ディレクトリ1、分散ロックとは何ですか? 2、redisによって実現する分散ロック 3、内部実現解析 3.1、redisにおけるデータ変化 3.2、redissonの実現方式 1、分散ロックとは何ですか.
分散ロックは、分散システム間の共有リソースへの同期アクセスを制御する方法です.分散システムでは、各システム間の動作を調整する必要があることが多い.異なるシステムまたは同じシステムの異なるホスト間で1つまたは複数のリソースが共有されている場合、これらのリソースにアクセスする際には、互いに干渉しないように反発して一貫性を保証する必要があり、この場合、分散ロックを使用する必要があります.
2、redis実現の分布式ロック
redissonフレームワークを使用してredisコマンドを操作します.redissonはredlockアルゴリズムを実現し,非同期,公平性などの高度な機能を提供した.maven依存を追加します.
JAvaが実現したシングルマシンモードredisに基づく分散ロックは,クラスタモードがこのモードと類似しており,信頼性が高い
output
3、内部実現解析
3.1.redisにおけるデータ変化
以org.redisson.RedissonLockのこの実装を解読します.上記のテストコード実行プロセスでは、redisにhashタイプの値が表示されます.ここで、「redis」はロック名であり、「redis」にはキー値のペアがあり、キーは「7 b 319 fef-0674-4 a 62-9 a 5 b-13 d 786 f 4 a 99 e:137」であり、値は「1」です.
ここで、キーのフォーマットRedissonLockクラスは以下のようにUUID+threadId、valueは再入値となる.redissonは再入可能な分散ロックを実現し,この場合valueは実際の再入可能な値に対応する.
redisのデータ
3.2.redissonの実現方式
主な実装はluaスクリプトによって実装され,luaスクリプト自体は原子性をサポートする.解析org.redisson.RedissonLockでロックを試みたプロセス.
ロックプロセス
ロック解除プロセス
分散ロックは、分散システム間の共有リソースへの同期アクセスを制御する方法です.分散システムでは、各システム間の動作を調整する必要があることが多い.異なるシステムまたは同じシステムの異なるホスト間で1つまたは複数のリソースが共有されている場合、これらのリソースにアクセスする際には、互いに干渉しないように反発して一貫性を保証する必要があり、この場合、分散ロックを使用する必要があります.
2、redis実現の分布式ロック
redissonフレームワークを使用してredisコマンドを操作します.redissonはredlockアルゴリズムを実現し,非同期,公平性などの高度な機能を提供した.maven依存を追加します.
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>3.11.1version>
dependency>
JAvaが実現したシングルマシンモードredisに基づく分散ロックは,クラスタモードがこのモードと類似しており,信頼性が高い
public class RedisConfig {
private static int count = 0; //
private String address = "127.0.0.1:6379";
private String password = "961113";
// private String poolSize= "20";
// private String database= "10";
public static void main(String[] args) throws InterruptedException {
String address = "redis://127.0.0.1:6379";
Config config = new Config();
config.useSingleServer().setAddress(address).setPassword("961113");
RedissonClient redisson = Redisson.create(config);
System.out.println(redisson);
int nThreads = 500; //
ExecutorService exec = Executors.newFixedThreadPool(nThreads);
List tasks = new ArrayList(nThreads);
for (int i = 0; i < nThreads; i++) {
tasks.add((Callable) () -> {
Thread.sleep(10); //
RLock lock = redisson.getLock("redis");
try {
boolean b = lock.tryLock(60, TimeUnit.SECONDS);
if (b) {
++RedisConfig.count;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//
lock.unlock();
}
return RedisConfig.count;
});
}
exec.invokeAll(tasks);
exec.shutdown();
while (true) {
if (exec.isTerminated()) {
System.out.println(" !");
break;
}
}
System.out.println("count = " + count);
redisson.shutdown();
}
}
output
org.redisson.Redisson@1672fe87
!
count = 500
3、内部実現解析
3.1.redisにおけるデータ変化
以org.redisson.RedissonLockのこの実装を解読します.上記のテストコード実行プロセスでは、redisにhashタイプの値が表示されます.ここで、「redis」はロック名であり、「redis」にはキー値のペアがあり、キーは「7 b 319 fef-0674-4 a 62-9 a 5 b-13 d 786 f 4 a 99 e:137」であり、値は「1」です.
ここで、キーのフォーマットRedissonLockクラスは以下のようにUUID+threadId、valueは再入値となる.redissonは再入可能な分散ロックを実現し,この場合valueは実際の再入可能な値に対応する.
protected String getLockName(long threadId) {
return id + ":" + threadId;
}
redisのデータ
127.0.0.1:6379> TYPE redis
hash
127.0.0.1:6379> HGETALL redis
1) "7b319fef-0674-4a62-9a5b-13d786f4a99e:137"
2) "1"
127.0.0.1:6379> HGETALL redis
1) "7b319fef-0674-4a62-9a5b-13d786f4a99e:95"
2) "1"
127.0.0.1:6379> HGETALL redis
(empty list or set)
127.0.0.1:6379> HGETALL redis
1) "7b319fef-0674-4a62-9a5b-13d786f4a99e:93"
2) "1"
3.2.redissonの実現方式
主な実装はluaスクリプトによって実装され,luaスクリプト自体は原子性をサポートする.解析org.redisson.RedissonLockでロックを試みたプロセス.
ロックプロセス
// KEYS[1] key, “redis”
// ARGV[1] , 30s。
// ARGV[2] set (uuid + threadId), 。
// key , hset (hset REDLOCK_KEY uuid+threadId 1), pexpire ( )
// KEY , value , , 1,
// KEY
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
"redis.call('pexpire', KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}
ロック解除プロセス
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
// , value , ,
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
// , ,value , ,
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
// 0 , ,
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
// 0 , key, 。
"redis.call('del', KEYS[1]); " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; "+
"end; " +
"return nil;",
// 5 KEYS[1],KEYS[2],ARGV[1],ARGV[2] ARGV[3]
Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
/**
* 、 、 。 !
*/