Redisson 分布式锁

Redisson 是Redis官方推荐的一个企业级开源Redis Client。提供了分布式锁的支持。

1 基本用法

maven依赖:

XML 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.24.3</version>
</dependency>
java 复制代码
   public void buy(Integer num) throws InterruptedException {
        String name = Thread.currentThread().getName();
        RLock lock = redissonClient.getLock(goods.getName());
        // 尝试获取锁 2 为获取锁最长等待时间,30为持有锁最长时间
        boolean isLocked = lock.tryLock(2, 30, TimeUnit.SECONDS);
        if (isLocked) {
            if (goods.getCount() >= num) {
                System.out.println(name + ",购买:" + num);
                Thread.sleep(4000);
                goods.setCount(goods.getCount() - num);
                System.out.println(goods);
            } else {
                System.out.println(name + "商品库存不足");
            }
        } else {
            System.out.println("获取锁失败");
        }
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

1.1 锁类型

Redisson 提供了多种分布式锁实现,有以下主要的锁类型:

  1. 可重入锁。支持锁重入,同一个线程可以多次获得同一把锁。
  2. 公平锁,按照请求的顺序获取锁,避免线程饥饿问题。
  3. 读写锁,读写分离的锁。
  4. 信号量,限制同时访问资源的线程数量。
  5. 联锁,将多个锁对象关联为一个锁,所有锁都成功获取才算成功,否则失败。用于对多资源原子性加锁。
  6. 红锁,通过在多个独立的Redis节点上获取锁来提高可靠性。有以下特性:a) 多数派原则:在N个节点上获取锁,成功获取(N/2 + 1)个才算成功。b)高可用性:容忍部分节点故障。

红锁的使用场景有:

  1. 高可用性要求:金融交易、支付系统等。
  2. 关键业务:不能容忍锁失败的业务场景。

2 Redisson 分布式锁原理

Redisson 使用Lua脚本来保证加锁操作的原子性。

采用了Redis的Hash结构存储锁信息。key 是锁名称,entry-name是客户端ID+线程ID。entry-value 是锁重入次数。通过判断key是否存在来确定是否获得锁。

  1. 当锁被其他客户端持有时,当前客户端的操作:a)获取锁的剩余生存时间。b)通过自旋等方式等待。c)通过Redis的pub/sub订阅解锁消息通道。
  2. 解锁通知机制:a)锁释放时会通过pub/sub发布消息到订阅频道。b)等待中的客户端收到通知后会立即尝试获取锁,避免无效轮询。

2.1 缺陷

Redisson 虽然提供了丰富的锁类型和强大的功能,但在实际使用中仍然存在一些缺陷和潜在问题。

1 主从架构下锁失效问题

当主节点解锁后未同步到从节点就宕机,从节点提升为主节点后,可能导致多个客户端同时持有锁。

解决:使用红锁或使用Zookeeper替代(会牺牲性能)。

2 看门狗机制导致的死锁问题

看门狗机制会定期(默认10s)续期锁(重置为30s),如果程序异常终止或网络问题导致锁未正确释放,锁会被无限续期。

解决:a)合理设置锁超时时间,避免使用默认的看门狗机制。b)确保锁释放操作放在finally块。

java 复制代码
finally {
    // 正确释放锁的方式
    if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

3 性能问题

每次获取锁都需要Redis网络通信。高并发下Redis可能成为瓶颈,锁竞争激烈时性能下降明显。

3 实战

通过分层命名设计和锁粒度控制实现锁的精准控制,同时避免命名冲突。

分层命名设计:[环境]:[模块]:[业务类型]:[操作类型]:[资源标识]

环境:prod/dev等

模块:order/goods等

业务类型:/check/deduct等

操作类型:lock/read/write

资源标识:id

1 、资源标识细化。

单资源锁:针对单一资源。

多资源锁:针对多个资源。

通配符锁:针对一类资源。需谨慎使用以避免锁范围过大。

2、操作类型区分。

写操作:使用write或deduct等明确标识。

读操作:使用read标识,并结合读锁实现读写分离,提升并发性能。

3.1 读写锁分离

统一资源锁,例如对商品库存的修改、查询及其他有关库存的业务使用同一个锁名,这样虽然能绝对互斥,但是并发性能差。

读写锁分离,多个读操作可并行,写操作独占资源,读写互斥。

通过一个资源注册中心类来规范锁的命名。

Redission的获取锁及释放锁操作,可以提取为一个注解,通过aop来对使用改注解的方法执行对应操作。但是这样锁的粒度是整个方法了。

相关推荐
带刺的坐椅4 分钟前
Solon v3.4.7, v3.5.6, v3.6.1 发布(国产优秀应用开发框架)
java·spring·solon
四谎真好看1 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程2 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t2 小时前
ZIP工具类
java·zip
lang201509282 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan3 小时前
第10章 Maven
java·maven
百锦再3 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说4 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多4 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring
百锦再4 小时前
对前后端分离与前后端不分离(通常指服务端渲染)的架构进行全方位的对比分析
java·开发语言·python·架构·eclipse·php·maven