Redisson分散ロック実戦(Redis高同時シーンに適用)

25195 ワード

実装方式1:投げ異常後lock値が0に戻らないという問題がある
@Autowired
private StringRedisTemplate stringRedisTemplate;

 @RequestMapping("deduct_stock")
 public void deductStock(){
     Long num = stringRedisTemplate.opsForValue().increment("lock", 1);
     //                num    1            1
     if (num==1){
         int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
         if (stock>0){
             stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");
             System.out.println("    ,  stock:"+(stock-1)+"");
         }else{
             System.out.println("    ,    ");
         }
     }
     //  
     stringRedisTemplate.opsForValue().increment("lock",-1);
 }

実現方式2:try...finallyを加える。プログラムが異常を投げ出すかどうかにかかわらず、最終的には復元されます。しかし、0に復元するとredisが停止したり、プログラムがtryコードブロックの内容を実行していないか、webアプリケーション全体が停止し、finallyブロックの内容も実行できない可能性があります。プログラムが再起動しても、後続のスレッドはnum=1の条件を満たすことができません。
@Autowired
private StringRedisTemplate stringRedisTemplate;

@RequestMapping("deduct_stock")
public void deductStock(){
    try{
        Long num = stringRedisTemplate.opsForValue().increment("lock", 1);
        //                num    1            1
        if (num==1){
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock>0){
                stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");
                System.out.println("    ,  stock:"+(stock-1)+"");
            }else{
                System.out.println("    ,    ");
            }
        }
    }finally {
        //  
        stringRedisTemplate.opsForValue().increment("lock",-1);
    }
}

実装方式3:lockのタイムアウト時間を設定する。ある時間、ネットワーク環境が悪いため、10秒以内に実行すべきプログラムが15秒で完了し、ロックが期限切れになったことは、後のスレッドがロックを手に入れることができ、ロックの形が虚構であることを意味するシーンがある。1番目のスレッドが15秒長く実行され、2番目のスレッドが8秒長く実行されたと仮定すると、ロックが失効したため、2番目のスレッドは新しいロックを再取得することができ、結果として1番目のスレッドは0に戻る操作でロックを解除すると、自分のロックが失効し、解放されたのは自分のロックではなく、2番目のスレッドのロックである。要するに、ロックが無効になる一連のシーンが存在する。
@Autowired
private StringRedisTemplate stringRedisTemplate;

@RequestMapping("deduct_stock")
public void deductStock(){
    try{
        String lockkey = "lock";
        Long num = stringRedisTemplate.opsForValue().increment(lockkey, 1);
        // key             
        stringRedisTemplate.expire(lockkey,10, TimeUnit.SECONDS);
        //                num    1            1
        if (num==1){
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock>0){
                stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");
                System.out.println("    ,  stock:"+(stock-1)+"");
            }else{
                System.out.println("    ,    ");
            }
        }
    }finally {
        //  
        stringRedisTemplate.opsForValue().increment("lock",-1);
    }
}

このシーンでは、現在のスレッドにバインドされたスレッドを開くことができます。ロックの失効時間が10秒であれば、5秒おきにこのロックをスキャンして、ロックがまだあるかどうかを見てみましょう。また、失効時間を再び10秒にリセットして、遅延を続けている場合は、現在のスレッドが直接このロックを解放するまで、このロックに自動遅延機能を備えさせます。そうしないと、一致して遅延します。

Redissonフレームワークによる分散ロック
上記の分析はRedissonの実現原理であり,ただ1つのスレッドを増やし,待機するスレッドに絶えずロックを試みさせ,whileサイクルによって実現される,通称スピンロックである.

スタンドアロンRedisの設定
/**
 * @ProjectName springbootdemo_src
 * @ClassName RedissionConfig
 * @Desicription TODO
 * @Author Zhang Xueliang
 * @Date 2019/7/27 17:34
 * @Version 1.0
 **/
@Configuration
public class RedissonConfig {
    @Bean("redisson")//    value            bean       
    public Redisson getRedisson(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

ロックコード:簡単です。3行のコードです。重要なのは運転原理を理解することです。
/**
 * @ProjectName springbootdemo_src
 * @ClassName RedissionController
 * @Desicription TODO
 * @Author Zhang Xueliang
 * @Date 2019/7/27 15:55
 * @Version 1.0
 **/

@SuppressWarnings("all")
@RestController
public class RedissionController {

    @Autowired
    private Redisson redisson;

    @RequestMapping("redisson_lock")
    public void redissonDeductStock() {
        String lockkey = "lock";
        RLock lock = redisson.getLock(lockkey);
        try {
            lock.lock();//         lock             30              
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stringRedisTemplate.opsForValue().set("stock", (stock - 1) + "");
                System.out.println("    ,  stock:" + (stock - 1) + "");
            } else {
                System.out.println("    ,    ");
            }
        } finally {
            lock.unlock();
        }
    }

}

Redissonは実質的にコードに悲観的なロックを加えたが,悲観的なロックは性能に影響を及ぼしている。incrementが1を増加させる方式は実質的に楽観的なロックである。Redisは生まれながらにして単一スレッドであり,高同時環境では主従クラスタに一定の問題がある.