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