redis实现分布式锁问题

现在的多数电商公司都会采用分布式架构,分布式部署,如下单,简单的描述

拿订单秒杀模块举例,通常我们实现秒杀会采用redis缓存技术,活动举行前把相应的库存数存储到redis里边

由图看以看出,在高并发的场景下,很容易造成库存的不一致问题。传统的锁像 synchronized 在此场景下就会失效。

此时,我们需要一把锁,在多个线程下都能上锁。

思考:

  • redis的 setnx方法:redis执行setnx 添加数据时,若redis中存在相同的key,那么redis中的数据不会发生变化。
  • redis是单线程的

那么,我们是不是可以用setnx来实现一把分布式锁呢?可以的,直接上代码

    /**
     * 模拟订单库存接口 秒杀
     *
     * @return
     */
    @RequestMapping
    public String testStock() {
        String lockKey = "testStock";
        String lockValue = UUID.randomUUID().toString();
        // 获取锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
        if (!lock) {
            return "服务器繁忙 请稍后";
        }
        try {
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock < 0) {
                //todo 业务逻辑
                return "库存不足";
            }
            //todo 业务逻辑
            int currStock = stock - 1;
            redisTemplate.opsForValue().set("stock", currStock + "");
            return "扣款成功 当前库存" + currStock;
        } finally {
            // 判断是否是当前线程的锁
            if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
                //释放锁
                redisTemplate.delete(lockKey);
            }
        }
    }

此时,这把分布式锁可以说是较为完善了,那么我们再来思考一个问题  

我们获取一把30s的锁是能保证30s内此线程能完成此接口的业务逻辑呢?万一遇到mysql慢查询?一条sql执行一分钟?

采取解决解决方案:锁续命

因为分布式问题存在比较多的不唯一性,实现起来较为麻烦。此时我们引入  redisson 来解决分布式锁的问题

redisson介绍

引入依赖

         <!-- redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.5</version>
        </dependency>

配置类

@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private String port;

    @Bean
    public Redisson redisson() {
        final String address = "redis://" + host + ":" + port;
        Config config = new Config();
        config.useSingleServer()
                .setAddress(address)
                .setTimeout(3000)
                .setPingConnectionInterval(1000)
                .setDatabase(0);
        return (Redisson) Redisson.create(config);
    }
}

使用redisson获取分布式锁

 @RequestMapping
    public String testStock() {
        String lockKey = "testStock";
        RLock lock = redisson.getLock(lockKey);
        try {
            boolean tryLock = lock.tryLock(0L, TimeUnit.SECONDS); //获取锁
            if (!tryLock) {
                return "服务器繁忙";
            }
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock < 0) {
                //todo 业务逻辑
                return "库存不足";
            }
            //todo 业务逻辑
            int currStock = stock - 1;
            redisTemplate.opsForValue().set("stock", currStock + "");
            return "扣款成功 当前库存" + currStock;
        } finally {
            lock.unlock();//释放锁
        }
    }

redisson lock.lock()方法底层也是获取一把过期时间为30s的锁,帮我们实现了锁续命的问题。(自行阅读代码,就不赘述了)

推荐阅读

解决redisTemplate存储 储存对象乱码的解决

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值