Redisシリーズ-生産応用編-分布式ロック(5)-単一プロセスRedis分布式ロックのJava実装(Redisson使用と下位実装)-原子ロッククラス
15962 ワード
Redisson単一プロセスRedis分布式楽観ロックの使用と実現
本論文はRedisson 3.7.5に基づく
4.原子鎖類
Redissonでは、RAtomicLongとRAtomicDouble、RLongAdderとRDoubleAdderの2つの原子ロッククラスが実現されています.
RAtomicDoubleとRAtomicLongは実は同じで、RLongAdderとRDoubleAdderは実は原理も同じで、ここではRAtomicLongとRLongAdderだけを言います.
4.1. RedissonAtomicLong-Redisに基づく原子Longクラス
原子類のincrementAndGet,decrementAndGet,addandGetは,主にINCR,DECR,INCRBY,DECRBYによって実現されているが,実際にはredisのこれらの操作自体が原子性である.
ではCASの更新は?luaスクリプトの特性を利用することができます.すなわち、redisは単一スレッドであり、同時に1つのluaスクリプトしか処理できないため、luaスクリプトは原子性を持っています.
4.2. RedissonLongAdder Redisに基づくLongAdder
統計シーンでは(書き込みが多くて読み取りが少なく、数値は同時安全を考慮する必要はありません)、LongAdderの表現はAtomicLongよりも優れていますが、redisベースではどのように実現されますか?Redissonの実装構想は比較的簡単で,ローカルにlongAdderを1つ残し,getまたはsumを呼び出す場合にのみ,ローカルのlongAdderの数値をredisに加算する.
統計的でgetされていない操作は、ローカルlongAdder操作の場合です.
getおよびsumに関連する操作は、ローカルlongAdderの値をredisに追加します.
本論文はRedisson 3.7.5に基づく
4.原子鎖類
Redissonでは、RAtomicLongとRAtomicDouble、RLongAdderとRDoubleAdderの2つの原子ロッククラスが実現されています.
RAtomicDoubleとRAtomicLongは実は同じで、RLongAdderとRDoubleAdderは実は原理も同じで、ここではRAtomicLongとRLongAdderだけを言います.
4.1. RedissonAtomicLong-Redisに基づく原子Longクラス
原子類のincrementAndGet,decrementAndGet,addandGetは,主にINCR,DECR,INCRBY,DECRBYによって実現されているが,実際にはredisのこれらの操作自体が原子性である.
@Override
public RFuture<Long> getAndAddAsync(final long delta) {
//getAndAdd INCRBY
return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, new RedisStrictCommand<Long>("INCRBY", new SingleConvertor<Long>() {
@Override
public Long convert(Object obj) {
return ((Long) obj) - delta;
}
}), getName(), delta);
}
@Override
public RFuture<Long> getAndSetAsync(long newValue) {
//getAndSet GetSet
return commandExecutor.writeAsync(getName(), LongCodec.INSTANCE, RedisCommands.GETSET, getName(), newValue);
}
@Override
public RFuture<Long> incrementAndGetAsync() {
//incrementAndGet INCR
return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.INCR, getName());
}
@Override
public RFuture<Long> decrementAndGetAsync() {
// DECR
return commandExecutor.writeAsync(getName(), StringCodec.INSTANCE, RedisCommands.DECR, getName());
}
ではCASの更新は?luaスクリプトの特性を利用することができます.すなわち、redisは単一スレッドであり、同時に1つのluaスクリプトしか処理できないため、luaスクリプトは原子性を持っています.
@Override
public RFuture<Boolean> compareAndSetAsync(long expect, long update) {
//CAS
// lua ,lua
// , , true, false
return commandExecutor.evalWriteAsync(getName(), StringCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local currValue = redis.call('get', KEYS[1]); "
+ "if currValue == ARGV[1] "
+ "or (tonumber(ARGV[1]) == 0 and currValue == false) then "
+ "redis.call('set', KEYS[1], ARGV[2]); "
+ "return 1 "
+ "else "
+ "return 0 "
+ "end",
Collections.<Object>singletonList(getName()), expect, update);
}
4.2. RedissonLongAdder Redisに基づくLongAdder
統計シーンでは(書き込みが多くて読み取りが少なく、数値は同時安全を考慮する必要はありません)、LongAdderの表現はAtomicLongよりも優れていますが、redisベースではどのように実現されますか?Redissonの実装構想は比較的簡単で,ローカルにlongAdderを1つ残し,getまたはsumを呼び出す場合にのみ,ローカルのlongAdderの数値をredisに加算する.
public class RedissonLongAdder extends RedissonBaseAdder<Long> implements RLongAdder {
// RAtomicLong redis
private final RAtomicLong atomicLong;
// longAdder
private final LongAdder counter = new LongAdder();
}
統計的でgetされていない操作は、ローカルlongAdder操作の場合です.
@Override
public void add(long x) {
counter.add(x);
}
@Override
public void increment() {
add(1L);
}
@Override
public void decrement() {
add(-1L);
}
getおよびsumに関連する操作は、ローカルlongAdderの値をredisに追加します.
@Override
protected RFuture<Long> addAndGetAsync() {
return atomicLong.getAndAddAsync(counter.sum());
}
@Override
protected RFuture<Long> getAndDeleteAsync() {
return atomicLong.getAndDeleteAsync();
}
@Override
public long sum() {
return get(sumAsync());
}