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来对使用改注解的方法执行对应操作。但是这样锁的粒度是整个方法了。

相关推荐
源码_V_saaskw14 分钟前
JAVA国际版任务悬赏+接单系统源码支持IOS+Android+H5
android·java·开发语言·javascript·微信小程序
双力臂4042 小时前
Java IO流体系详解:字节流、字符流与NIO/BIO对比及文件拷贝实践
java·开发语言·nio
钮钴禄·爱因斯晨2 小时前
Java API (二):从 Object 类到正则表达式的核心详解
java·开发语言·信息可视化·正则表达式
就叫飞六吧2 小时前
maven本地仓库清缓存py脚本
spring·缓存·maven
Monkey-旭3 小时前
Android 蓝牙通讯全解析:从基础到实战
android·java·microsoft·蓝牙通讯
眠修3 小时前
部署 Zabbix 企业级分布式监控
笔记·分布式·zabbix
付出不多3 小时前
Zabbix企业级分布式监控
分布式·zabbix
Pseudo…3 小时前
部署zabbix企业级分布式监控
分布式·zabbix
BoneToBone3 小时前
java list 与set 集合的迭代器在进行元素操作时出现数据混乱问题及原因
java·开发语言·list
TinpeaV3 小时前
Elasticsearch / MongoDB / Redis / MySQL 区别
大数据·redis·mysql·mongodb·elasticsearch