欢迎光临散文网 会员登陆 & 注册

这可能是B站讲的最好的Redis实战教程(2023年最新版)

2023-02-24 11:31 作者:眼角的一缕阳光  | 我要投稿

synchronized只能保证在单一tomcat容器内线程安全,若是分布式微服务并发访问这个接口,还是会出现超卖的问题

@RestController

public class Indexcontroller {

@Autowired

private Redisson redisson;

@Autowired

private StringRedisTemplate stringRedisTemplate;

@RequestMapping("/deduct_stock")

public string deductstock(){

synchronized (this) {

int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")

if (stock >0){

int realStock = stock - 1;

stringRedisTemplate.opsForValue().set("stock",realStock + "");//jedis.set(key , value)

System.out.println("扣减成功,剩余库存:" + realStock);

}else {

System.out.println("扣减失败,库存不足");

}

}

return "end";

}

}


解决方案--分布式锁

@RestController

public class Indexcontroller {

@Autowired

private Redisson redisson;

@Autowired

private StringRedisTemplate stringRedisTemplate;

@RequestMapping("/deduct_stock")

public string deductstock(){

//分布式锁的key值

String lockKey = "lock:product:101";

//分布式锁

//设置key超时时间,避免系统执行业务时宕机导致死锁

boolean result= stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zhuge",10,TimeUnit.SECONDS);

if(!result){

return "error_code";

}

try{

//获取库存

int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")

if (stock >0){

int realStock = stock - 1;

stringRedisTemplate.opsForValue().set("stock",realStock + "");//jedis.set(key , value)

System.out.println("扣减成功,剩余库存:" + realStock);

}else {

System.out.println("扣减失败,库存不足");

}

}finally{

//释放分布式锁

stringRedisTemplate.delete(lockKey);

}

return "end";

}

}

新问题:1、在10s时间内,库存操作还没有完成,但分布式锁已经超时释放,当执行释放锁的操作时,会报空指针;

2、当线程1在线程2执行加锁之后,将锁释放,会导致线程2的分布式锁失效

解决方法如下:

public class Indexcontroller {

@Autowired

private Redisson redisson;

@Autowired

private StringRedisTemplate stringRedisTemplate;

@RequestMapping("/deduct_stock")

public string deductstock(){

//分布式锁的key值

String lockKey = "lock:product:101";

//区分是不同的请求来保证分布式锁

String clientId = UUID.randomUUID().toString();

//分布式锁

//设置key超时时间,避免系统执行业务时宕机导致死锁

boolean result= stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10,TimeUnit.SECONDS);

if(!result){

return "error_code";

}

try{

//获取库存

int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")

if (stock >0){

int realStock = stock - 1;

stringRedisTemplate.opsForValue().set("stock",realStock + "");//jedis.set(key , value)

System.out.println("扣减成功,剩余库存:" + realStock);

}else {

System.out.println("扣减失败,库存不足");

}

}finally{

if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){

//释放分布式锁

stringRedisTemplate.delete(lockKey);

}

}

return "end";

}

}

新问题:如果当线程1请求9.9s之后执行到31行代码,下一个请求在设置key的瞬间,线程1执行33行代码,导致下一次请求锁失效;

所以释放分布式锁需保证原子性。以上所有问题都是由key的失效时间导致,为解决这个问题出现了锁续命方法,在加锁

之后开启一个子线程执行每10s的时间间隔内来判断锁是否存在,如果存在,将锁超时时间设置为30s。


最强分布式锁工具:Redisson

<dependency>

<groupId>org.redisson</groupId>

<artifactId>redisson</artifactId>

<version>3.11.1</version>

</dependency>

public class Indexcontroller {

@Autowired

private Redisson redisson;

@Autowired

private StringRedisTemplate stringRedisTemplate;

@RequestMapping("/deduct_stock")

public string deductstock(){

//分布式锁的key值

String lockKey = "lock:product:101";

//Redisson锁对象

RLock redissonLock = redisson.getLock(lockKey);

//加锁

redissonLock.lock();

try{

//获取库存

int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));//jedis.get("stock")

if (stock >0){

int realStock = stock - 1;

stringRedisTemplate.opsForValue().set("stock",realStock + "");//jedis.set(key , value)

System.out.println("扣减成功,剩余库存:" + realStock);

}else {

System.out.println("扣减失败,库存不足");

}

}finally{

//释放分布式锁

redissonLock.lock();

}

return "end";

}

}


问题:当java程序加锁之后,redis主节点直接宕机,分布式锁信息被没有同步给从节点,如何保证redis分布式锁不失效?

RedLock可以解决锁丢失问题(zookeeper也可以实现分布式锁)

RedLock实现原理

@RestController

public class IndexController {

@Resource

private Redisson redisson;

@Resource

private StringRedisTemplate stringRedisTemplate;


@RequestMapping("/deduct_stock")

public String deductStock(){

//分布式锁的key值

String lockKey = "lock:product:101";

//Redisson锁对象

RLock redissonLock = redisson.getLock(lockKey);

//加锁

redissonLock.lock();

try{

//获取库存 //相当于jedis.get("stock")

int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));

if (stock >0){

int realStock = stock - 1;

//相当于jedis.set(key , value)

stringRedisTemplate.opsForValue().set("stock",realStock + "");

System.out.println("扣减成功,剩余库存:" + realStock);

}else {

System.out.println("扣减失败,库存不足");

}

}finally{

//释放分布式锁

redissonLock.lock();

}


return "end";

}


@RequestMapping("/redlock")

public String redlock(){

//分布式锁的key值

String lockKey = "lock:product:101";

//这里需要自己实例化不同redis实例的redisson客户端连接,这里只是伪代码用一个redisson客户端简化了

RLock lock1 = redisson.getLock(lockKey);

RLock lock2 = redisson.getLock(lockKey);

RLock lock3 = redisson.getLock(lockKey);

//根据多个 RLock 对象构建 RedissonRedLock (最核心的差别就在这里)

RedissonRedLock redLock = new RedissonRedLock(lock1,lock2,lock3);

try {

/**

* waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败

* LeaseTime锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)

*/

boolean res = redLock.tryLock(10, 30, TimeUnit.SECONDS);

if (res) {

//成功获得锁,在这里处理业务

}

} catch (Exception e) {

throw new RuntimeException("lock fail");

} finally {

//无论如何,最后都要解锁

redLock.unlock();

}

return "end";

}

}

这可能是B站讲的最好的Redis实战教程(2023年最新版)的评论 (共 条)

分享到微博请遵守国家法律