Redis

关注公众号 jb51net

关闭
首页 > 数据库 > Redis > Redis分布式锁超卖

Redis分布式锁解决超卖问题

作者:Eliauk-_-

超卖问题是典型的多线程安全问题,本文就来介绍一下Redis分布式锁解决超卖问题,具有一定的参考价值,感兴趣的可以了解一下

一、使用redisTemplate中的setIfAbsent方法。

      // setIfAbsent就是对应redis的setnx
        Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, lockKey, 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(setIfAbsent)) {
            LOG.info("恭喜,抢到锁了!lockKey:{}", lockKey);
        } else {
             LOG.info("很遗憾,没抢到锁!lockKey:{}", lockKey);
        }

缺点:

二、使用Redisson解决(看门狗方式)

2.1、实现原理

redisson在获取锁之后,会开启一个守护线程(看门狗线程),当锁即将过期还没有释放时,不断的延长锁key的生存时间

2.2、SpringBoot集成Redisson

2.2.1、添加pom.xml依赖

            <!--至少3.18.0版本,才支持spring boot 3-->
            <!--升级到3.20.0,否则打包生产会报错:Could not initialize class org.redisson.spring.data.connection.RedissonConnection-->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson-spring-boot-starter</artifactId>
                <version>3.21.0</version>
            </dependency>

2.2.2、注入RedissonClient对象

@Autowired
private RedissonClient redissonClient;

2.2.3、使用Redisson

RLock lock = null;
   try {
            // 使用redisson,自带看门狗
             lock = redissonClient.getLock(lockKey);
             /**
               waitTime – the maximum time to acquire the lock 等待获取锁时间(最大尝试获得锁的时间),超时返回false
               leaseTime – lease time 锁时长,即n秒后自动释放锁
               time unit – time unit 时间单位
              */
             // boolean tryLock = lock.tryLock(30, 10, TimeUnit.SECONDS); // 不带看门狗
             boolean tryLock = lock.tryLock(0, TimeUnit.SECONDS); // 带看门狗
             if (tryLock) {
                 LOG.info("恭喜,抢到锁了!");
             } else {
                 LOG.info("很遗憾,没抢到锁");
             }
   } catch (InterruptedException e) {
             LOG.error("发生异常", e);
        } finally {
             LOG.info("释放锁!");
            //当lock不等于null并且lock是当前线程的时候去释放锁
             if (null != lock && lock.isHeldByCurrentThread()) {
                 lock.unlock();
             }
        }

缺点:

三、Redis红锁

2.1、Redis红锁名称来源

Redis 红锁的名称来源于 Redis 的logo,Redis 的 logo 是一个红色热气球,而红色的热气球上有一把锁的图案,因此这种分布式锁解决方案也被称为"Redlock",中文翻译为"红锁"。

2.2、原理

现在假设有5个Redis主节点(大于3的奇数个),这样基本保证他们不会同时都宕掉,获取锁和释放锁的过程中,客户端会执行以下操作:

2.3、代码实现

2.3.1、注册红锁的RedissonClient

@Component
public class RedisConfig {
	@Bean(name = "redissonClient1")
    @Primary
    public RedissonClient redissonRed1(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6379").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient2")
    public RedissonClient redissonRed2(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6380").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient3")
    public RedissonClient redissonRed3(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6381").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient4")
    public RedissonClient redissonRed4(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6382").setDatabase(0);
        return Redisson.create(config);
    }
    @Bean(name = "redissonClient5")
    public RedissonClient redissonRed5(){
        Config config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6383").setDatabase(0);
        return Redisson.create(config);
    }
  }

2.3.2、注入Redis红锁对象

    // 红锁
    @Autowired
    @Qualifier("redissonClient1")
    private RedissonClient redissonClient1;
    @Autowired
    @Qualifier("redissonClient2")
    private RedissonClient redissonClient2;
    @Autowired
    @Qualifier("redissonClient3")
    private RedissonClient redissonClient3;
    @Autowired
    @Qualifier("redissonClient4")
    private RedissonClient redissonClient4;
    @Autowired
    @Qualifier("redissonClient5")
    private RedissonClient redissonClient5;

2.3.3、使用Redis红锁

        /*
           假设有五台redis机器, A B C D E
           线程1: A B C D E(获取到锁)
           线程2: C D E(获取到锁)
           线程3: C(未获取到锁)
        */
            RLock lock1 = null;
            RLock lock2 = null;
            RLock lock3 = null;
            RLock lock4 = null;
            RLock lock5 = null;
        try {
            lock1 = redissonClient1.getLock(lockKey);
            lock2 = redissonClient2.getLock(lockKey);
            lock3 = redissonClient3.getLock(lockKey);
            lock4 = redissonClient4.getLock(lockKey);
            lock5 = redissonClient5.getLock(lockKey);
            // 红锁的写法
            RedissonRedLock redissonRedLock = new RedissonRedLock(lock1, lock2, lock3, lock4, lock5);
            boolean tryLock = redissonRedLock.tryLock(0, TimeUnit.SECONDS);
            if (tryLock) {
                LOG.info("恭喜,抢到锁了!");
            } else {
                LOG.info("很遗憾,没抢到锁");
            }
        } catch (InterruptedException e) {
            LOG.error("发生异常", e);
        } finally {
            LOG.info("释放锁!");
            //当lock不等于null并且lock是当前线程的时候去释放锁
            if (null != lock && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

注意:

到此这篇关于Redis分布式锁解决超卖问题的文章就介绍到这了,更多相关Redis分布式锁超卖内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文