前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 分布式锁

Redis 分布式锁

作者头像
张云飞Vir
发布2022-09-29 12:21:38
4560
发布2022-09-29 12:21:38
举报
文章被收录于专栏:写代码和思考

场景

一般电商网站都会遇到秒杀、特价之类的活动,大促活动有一个共同特点就是访问量激增,在高并发下会出现成千上万人抢购一个商品的场景。虽然在系统设计时会通过限流、异步、排队等方式优化,但整体的并发还是平时的数倍以上,参加活动的商品一般都是限量库存,如何防止库存超卖,避免并发问题呢?分布式锁就是一个解决方案。

“分布式锁”是用来解决分布式应用中“并发冲突”的一种常用手段,实现方式一般有基于zookeeper及基于redis二种

讨论

1、无锁

先看下下面的代码,实现方式01,没有锁,当5个线程同时访问就乱了。

代码语言:javascript
复制
private String impl01() {
    String stockStr = stringRedisTemplate.opsForValue().get("stock");
    if (StringUtils.isEmpty(stockStr)) return "库存为空";
    int stock = Integer.parseInt(stockStr);
    if (stock > 0) {
        int newStock = stock - 1;
        stringRedisTemplate.opsForValue().set("stock", newStock + "");
        System.out.printf("扣减成功 from %s to %s \n", stock, newStock);
        return "扣减成功";
    } else {
        System.out.printf("扣减失败 from %s \n", stock);
        return "扣减失败";
    }
}

首先想到是加 synchronized。 而 synchronized 锁是属于JVM级别的,也就是在在单个机器上有效。而在微服务架构下,一般都是集群部署,一个服务会部署到不同计算机上。就又有问题了。

2、redis 分布式锁

使用SETNX实现分布式锁,

代码语言:javascript
复制
    try{
      // 先获得 锁,如果获得失败,则提示错误码
      Boolean lock_lock = stringRedisTemplate.opsForValue().setIfAbsent("LOCK_LOCK", "1");
      if (!lock_lock) return "获得锁失败,无法继续";

      ....这里是上面的业务代码。略

    }finally{
      // 完事后释放 锁
      stringRedisTemplate.delete("LOCK_LOCK");
    }

不过它也是有缺陷的: 超时 设置的10s时间 时,导致未完成任务,就把锁释放了,就乱套了。准确的10s真的合适吗, 如果在真正高并发的场景下,可能锁就会面临“一直失效”或“永久失效”。 1.存在请求释放锁时释放掉的并不是自己的锁 2.超时时间过短,存在代码未执行完便自动释放

3、Redisson实现分布式锁

Redisson 是一个连接 Redis 的客户端包。充分的利用了 Redis 键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。比如 锁 等。

引入依赖

代码语言:javascript
复制
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.6.5</version>
</dependency>

代码

代码语言:javascript
复制
private synchronized String impl06() {
       RLock rLock = redisson.getLock("LOCK_LOCK_2");
       try {
           rLock.lock();

           //----------------------
           String res = "";
           String stockStr = stringRedisTemplate.opsForValue().get("stock");
           if (StringUtils.isEmpty(stockStr)) return "库存为空";
           int stock = Integer.parseInt(stockStr);
           if (stock > 0) {
               int newStock = stock - 1;
               stringRedisTemplate.opsForValue().set("stock", newStock + "");
               res = String.format("扣减成功 from %s to %s \n", stock, newStock);
               System.out.println(res);
           } else {
               res = String.format("扣减失败 from %s \n", stock);
               System.out.println(res);
           }
           return res;
       } finally {
           // 完事后释放 锁
           rLock.unlock();
       }
   }

@Bean
RedissonClient Redisson() {
    Config config = new Config();
    config.useSingleServer()
            .setAddress("redis://192.168.1.111:6379")
            .setDatabase(1);
    return Redisson.create(config);
}

在思考解决方案时我们首先想到CAP原则(一致性、可用性、分区容错性),

  • 那么现在的Redis就是满足AP(可用性、分区容错性),
  • 如果想要解决该问题我们就需要寻找满足CP(一致性、分区容错性)的分布式系统。比如 zookeeper

并发量没有那么高,可以用zookeeper来做分布式锁,但是它的并发能力远远不如Redis。如果你对并发要求比较高的话,那就用Redis

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景
  • 讨论
    • 1、无锁
      • 2、redis 分布式锁
        • 3、Redisson实现分布式锁
        相关产品与服务
        云数据库 Redis®
        腾讯云数据库 Redis®(TencentDB for Redis®)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档