4、Redis高并发分布式锁实战

发布时间:2024年01月15日

引言

在分布式系统中,保证数据的一致性和避免竞争条件是至关重要的。分布式锁是一种常用的机制,而Redis作为一款高性能的内存数据库,提供了简单而强大的分布式锁方案。本文将深入探讨如何利用Redis高并发分布式锁来解决分布式系统中的并发控制问题,并提供实战案例。

正常库存扣减代码

public void deductStock(){
    int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
    if(stock>0){
        stock = stock -1 ;
        redisTemplate.opsForValue().set("stock",stock+"");
        System.out.println("扣减成功,剩余库存:"+stock);
    }else {
        System.out.println("扣减失败,库存不足");
    }
}
//弊端:两个线程同时执行读取stock为50,然后各自-1 修改为49,实际应该50-2=48

代码调整后

public void deductStock(){
    synchronized (this){
        int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
        if(stock>0){
            stock = stock -1 ;
            redisTemplate.opsForValue().set("stock",stock+"");
            System.out.println("扣减成功,剩余库存:"+stock);
        }else {
            System.out.println("扣减失败,库存不足");
        }
    }
}
//弊端:适用于单体项目,如果该项目被部署两台服务器,两台服务器同时访问获取stock为50,然后各自-1 修改为49,实际应该50-2=48 也会存在上述问题,因为synchronized只能在当前项目下生效

redis的一个简单的分布式锁

public void deductStock(){
    String lockKey = "lockKey";
    try {
        Boolean result = redisTemplate.opsForValue().setIfAbsent("lockKey", "nuoyi",10, TimeUnit.SECONDS);
        if(!result){
            System.out.println("....");
            return ;
        }
        int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
        if(stock>0){
            stock = stock -1 ;
            redisTemplate.opsForValue().set("stock",stock+"");
            System.out.println("扣减成功,剩余库存:"+stock);
        }else {
            System.out.println("扣减失败,库存不足");
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        redisTemplate.delete("lockKey");
    }
}
//弊端:适用于访问量不高的系统  如果访问量非常的大,第一个a请求获取到锁 ,设置过期10s,执行业务需要15s,a业务执行10s后锁自动过期被第二个请求b拿到并执行业务,当b业务执行到第5s时,b的锁被a的请求给释放了,如此高并发循环,导致锁失效

优化上述redis的分布式锁解决不是自己的锁不释放
?

public void deductStock(){
    String lockKey = "lockKey";
    String clientId = UUID.randomUUID().toString();
    try {
        Boolean result = redisTemplate.opsForValue().setIfAbsent("lockKey", clientId,10, TimeUnit.SECONDS);
        if(!result){
            System.out.println("....");
            return ;
        }
        int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
        if(stock>0){
            stock = stock -1 ;
            redisTemplate.opsForValue().set("stock",stock+"");
            System.out.println("扣减成功,剩余库存:"+stock);
        }else {
            System.out.println("扣减失败,库存不足");
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        //不是自己的锁不删除
        if(clientId.equals(redisTemplate.opsForValue().get(lockKey))){
            redisTemplate.delete("lockKey");
        }
    }
}
//不是自己的锁不删除,但是这个只是解决了a请求删除b请求的锁,如果a请求15秒锁第十秒过期了,b请求就进来了还是会有问题,解决方案:给锁续命

?Redisson代码
?

private final Redisson redisson;

public void deductStock(){
    String lockKey = "lockKey";
    RLock redissonLock = redisson.getLock(lockKey);//获取锁
    try {
        redissonLock.lock();//加锁及锁续命   默认锁失效30s  守护线程每10s续命一次
        int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
        if(stock>0){
            stock = stock -1 ;
            redisTemplate.opsForValue().set("stock",stock+"");
            System.out.println("扣减成功,剩余库存:"+stock);
        }else {
            System.out.println("扣减失败,库存不足");
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        redissonLock.unlock();//释放锁
    }
}
//三行代码即可满足获取锁、锁续命、释放锁,完美解决上述redis的释放锁及锁续命问题  redisson的底层还是redis,使用了大量的lua脚本,lua脚本支持原子性

redisson配置
?

@Bean
public Redisson redisson(){
    Config config = new Config();
    //useSingleServer 单机版
    config.useSingleServer().setAddress("redis://"+instance.getRedisHost()+":"+instance.getRedisPort()).setDatabase(instance.getRedisDataBase());
    return (Redisson)Redisson.create(config);
}


?

lua脚本语言:

  • 减少网络开销(批量操作)

  • 原子性

  • 替代redis的事务
    ?

为什么redis不常使用lua?

因为Redis是个单线程,如果lua有耗时运算或循环,Redis则阻塞,不会管其他的操作

通过学习本文,读者将深入了解Redis分布式锁的原理和实践应用。分布式锁在构建高并发、分布式系统中发挥着关键作用,正确使用和理解分布式锁是确保系统稳定性和可靠性的重要一环。希望本文能为读者提供有益的指导和实战经验。

文章来源:https://blog.csdn.net/qq_41973632/article/details/135605669
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。