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は生まれながらにして単一スレッドであり,高同時環境では主従クラスタに一定の問題がある.