redisトランザクションとredis分散ロック(悲観ロックと楽観ロック)
1.redisトランザクション-mutil/exec
redisではmutilがトランザクションの開始であり、execがトランザクションの終了である127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set hello 1
QUEUED
127.0.0.1:6379> set hello 2
QUEUED
127.0.0.1:6379> set hello 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> get hello
"3"
図:mutilコマンドを実行すると、トランザクションが開始することを示し、コマンド1、コマンド2、コマンド3に進み、順番にキューに入り、execまで待つと、すべてのキューのコマンドが順番に実行されます.途中で問題が発生すると、エラーメッセージが返されます.(ただしredisはロールバックメカニズムを提供していない.すなわち、上のset hello 2がエラーを実行してもset hello 3が実行され、最後にget helloの戻り値も3である)がjavaプログラムは、返されたエラー情報によって関連操作を実行することができる.
2.redisトランザクション-watch(watchの役割は楽観的なロックに似ている)
単一スレッドではmutil/execでトランザクションが保証されますが、マルチスレッドでは、次のコードでトランザクション(以下incr mykeyを実装するコード)が保証されません.2つのスレッドがval=val+1という行のコードに同時に入ると、元の1回のコードが2回加算される可能性があります.mutil
val = GET mykey
val = val + 1
SET mykey $val
exec
そこでwatchコマンドが現れ、WATCHコマンドは1つ以上のキーを監視することができ、そのうちの1つのキーが変更(または削除)されると、その後のトランザクションキューに保存されているコマンドは実行されず、EXECコマンドまで監視されます.(トランザクションキューのコマンドはEXECで実行されます)これにより、execの前にエラーが発生すると、エラー情報が返されることが保証されます.トランザクション・キュー内のコマンドはすべて実行解除されます.
次のコードでは、watchはトランザクションを実行する前にmykeyが所望の値と同じかどうかを比較し、mykeyが他の変更を受けていない場合は次の操作を実行し、そうでない場合はSET mykey$valを実行しない楽観的なロックメカニズムを提供します.WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
3.redisの分散ロック実装–悲観ロック
tryLockの本質は、あるkeyにSETNXコマンドを使用することです.詳細は次のとおりです.https://blog.csdn.net/qq_35688140/article/details/100923747 while (true) {
if (Integer.parseInt(jedis.get(key)) <= 0) {
break;// , ,
}
// , ,
System.out.println(" :" + clientName + " ");
if (redisBasedDistributedLock.tryLock(3,TimeUnit.SECONDS)) {// 3 , false( : )
int prdNum = Integer.parseInt(jedis.get(key));//
if (prdNum > 0) {
jedis.decr(key);//
jedis.sadd(clientList, clientName);//
System.out.println(" , :" + clientName + " ");
} else {
System.out.println(" , 0, :" + clientName + " ");
}
redisBasedDistributedLock.unlock0();//
break;
}
}
//
redisBasedDistributedLock = null;
RedisUtil.returnResource(jedis);
}
4.redisの分散ロック実装–楽観ロック
watch,mutil,execを用いて分散楽観ロックを実現した.while(true){
System.out.println(" :" + clientName + " ");
jedis = RedisUtil.getInstance().getJedis();
try {
jedis.watch(key);// , exec ,
int prdNum = Integer.parseInt(jedis.get(key));//
if (prdNum > 0) {
Transaction transaction = (Transaction) jedis.multi();// redis
((Jedis) transaction).set(key,String.valueOf(prdNum - 1));//
List<Object> result = ((redis.clients.jedis.Transaction) transaction).exec();// ( : key )
if (result == null || result.isEmpty()) {
System.out.println(" , :" + clientName + " ");// watch-key ,
}else {
jedis.sadd(clientList, clientName);//
System.out.println(" , :" + clientName + " ");
break;
}
}else {
System.out.println(" , 0, :" + clientName + " ");
break;
}
} catch (Exception e) {
// TODO: handle exception
}finally{
jedis.unwatch();
RedisUtil.returnResource(jedis);
}
}
参考文献:https://www.lizenghai.com/archives/21845.html https://www.jianshu.com/p/06f1bce98451 https://blog.csdn.net/Evankaka/article/details/70570200 https://blog.csdn.net/qw463800202/article/details/53287139 https://blog.csdn.net/wanderlustlee/article/details/81082506 https://www.jianshu.com/p/361cb9cd13d5 https://blog.csdn.net/wjq008/article/details/81207240
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set hello 1
QUEUED
127.0.0.1:6379> set hello 2
QUEUED
127.0.0.1:6379> set hello 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> get hello
"3"
単一スレッドではmutil/execでトランザクションが保証されますが、マルチスレッドでは、次のコードでトランザクション(以下incr mykeyを実装するコード)が保証されません.2つのスレッドがval=val+1という行のコードに同時に入ると、元の1回のコードが2回加算される可能性があります.
mutil
val = GET mykey
val = val + 1
SET mykey $val
exec
そこでwatchコマンドが現れ、WATCHコマンドは1つ以上のキーを監視することができ、そのうちの1つのキーが変更(または削除)されると、その後のトランザクションキューに保存されているコマンドは実行されず、EXECコマンドまで監視されます.(トランザクションキューのコマンドはEXECで実行されます)これにより、execの前にエラーが発生すると、エラー情報が返されることが保証されます.トランザクション・キュー内のコマンドはすべて実行解除されます.
次のコードでは、watchはトランザクションを実行する前にmykeyが所望の値と同じかどうかを比較し、mykeyが他の変更を受けていない場合は次の操作を実行し、そうでない場合はSET mykey$valを実行しない楽観的なロックメカニズムを提供します.
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
3.redisの分散ロック実装–悲観ロック
tryLockの本質は、あるkeyにSETNXコマンドを使用することです.詳細は次のとおりです.https://blog.csdn.net/qq_35688140/article/details/100923747 while (true) {
if (Integer.parseInt(jedis.get(key)) <= 0) {
break;// , ,
}
// , ,
System.out.println(" :" + clientName + " ");
if (redisBasedDistributedLock.tryLock(3,TimeUnit.SECONDS)) {// 3 , false( : )
int prdNum = Integer.parseInt(jedis.get(key));//
if (prdNum > 0) {
jedis.decr(key);//
jedis.sadd(clientList, clientName);//
System.out.println(" , :" + clientName + " ");
} else {
System.out.println(" , 0, :" + clientName + " ");
}
redisBasedDistributedLock.unlock0();//
break;
}
}
//
redisBasedDistributedLock = null;
RedisUtil.returnResource(jedis);
}
4.redisの分散ロック実装–楽観ロック
watch,mutil,execを用いて分散楽観ロックを実現した.while(true){
System.out.println(" :" + clientName + " ");
jedis = RedisUtil.getInstance().getJedis();
try {
jedis.watch(key);// , exec ,
int prdNum = Integer.parseInt(jedis.get(key));//
if (prdNum > 0) {
Transaction transaction = (Transaction) jedis.multi();// redis
((Jedis) transaction).set(key,String.valueOf(prdNum - 1));//
List<Object> result = ((redis.clients.jedis.Transaction) transaction).exec();// ( : key )
if (result == null || result.isEmpty()) {
System.out.println(" , :" + clientName + " ");// watch-key ,
}else {
jedis.sadd(clientList, clientName);//
System.out.println(" , :" + clientName + " ");
break;
}
}else {
System.out.println(" , 0, :" + clientName + " ");
break;
}
} catch (Exception e) {
// TODO: handle exception
}finally{
jedis.unwatch();
RedisUtil.returnResource(jedis);
}
}
参考文献:https://www.lizenghai.com/archives/21845.html https://www.jianshu.com/p/06f1bce98451 https://blog.csdn.net/Evankaka/article/details/70570200 https://blog.csdn.net/qw463800202/article/details/53287139 https://blog.csdn.net/wanderlustlee/article/details/81082506 https://www.jianshu.com/p/361cb9cd13d5 https://blog.csdn.net/wjq008/article/details/81207240
while (true) {
if (Integer.parseInt(jedis.get(key)) <= 0) {
break;// , ,
}
// , ,
System.out.println(" :" + clientName + " ");
if (redisBasedDistributedLock.tryLock(3,TimeUnit.SECONDS)) {// 3 , false( : )
int prdNum = Integer.parseInt(jedis.get(key));//
if (prdNum > 0) {
jedis.decr(key);//
jedis.sadd(clientList, clientName);//
System.out.println(" , :" + clientName + " ");
} else {
System.out.println(" , 0, :" + clientName + " ");
}
redisBasedDistributedLock.unlock0();//
break;
}
}
//
redisBasedDistributedLock = null;
RedisUtil.returnResource(jedis);
}
watch,mutil,execを用いて分散楽観ロックを実現した.
while(true){
System.out.println(" :" + clientName + " ");
jedis = RedisUtil.getInstance().getJedis();
try {
jedis.watch(key);// , exec ,
int prdNum = Integer.parseInt(jedis.get(key));//
if (prdNum > 0) {
Transaction transaction = (Transaction) jedis.multi();// redis
((Jedis) transaction).set(key,String.valueOf(prdNum - 1));//
List<Object> result = ((redis.clients.jedis.Transaction) transaction).exec();// ( : key )
if (result == null || result.isEmpty()) {
System.out.println(" , :" + clientName + " ");// watch-key ,
}else {
jedis.sadd(clientList, clientName);//
System.out.println(" , :" + clientName + " ");
break;
}
}else {
System.out.println(" , 0, :" + clientName + " ");
break;
}
} catch (Exception e) {
// TODO: handle exception
}finally{
jedis.unwatch();
RedisUtil.returnResource(jedis);
}
}
参考文献:https://www.lizenghai.com/archives/21845.html https://www.jianshu.com/p/06f1bce98451 https://blog.csdn.net/Evankaka/article/details/70570200 https://blog.csdn.net/qw463800202/article/details/53287139 https://blog.csdn.net/wanderlustlee/article/details/81082506 https://www.jianshu.com/p/361cb9cd13d5 https://blog.csdn.net/wjq008/article/details/81207240