springboot3 redisson 分布式锁入门与实战

Redisson 项目

https://gitee.com/supervol/loong-springboot-study

(记得给个start,感谢)

Redisson 介绍

        在分布式系统中,多节点部署的应用对共享资源(如数据库记录、缓存键、文件)的并发访问需要分布式锁来保证互斥性,避免数据不一致问题。Spring Boot 3 生态中,Redisson 是实现分布式锁的主流方案之一 —— 它基于 Redis 封装了完整的分布式锁功能,解决了原生 Redis 实现锁的诸多痛点(如手动续期、不可重入、释放他人锁等),提供了高可用、易扩展的分布式锁能力。

Redisson 核心

1. 为什么需要分布式锁

        单机应用中,synchronized 或 java.util.concurrent.locks.Lock 可解决并发问题,但分布式系统中多节点共享资源时,单机锁失效(各节点内存独立),需跨节点的 “全局锁”:

  • 核心需求:互斥性(同一时间仅一个节点持有锁)、安全性(避免死锁)、可用性(锁服务不宕机)、重入性(同一线程可重复获取锁)、公平性(可选,按请求顺序获取)。
  • 原生 Redis 痛点:需手动实现 setNx+expire 原子操作、手动处理锁续期、无法原生支持重入、可能释放他人锁,而 Redisson 封装了这些细节。

2. Redisson 是什么

        Redisson 是基于 Redis 的 Java 分布式客户端,不仅提供 Redis 基础操作(如字符串、哈希、列表),还内置了大量分布式场景工具类,核心能力包括:

  • 分布式锁(可重入锁、公平锁、读写锁等);
  • 分布式集合(分布式 Map、Set、Queue);
  • 分布式信号量、计数器、限流器;
  • 支持 Redis 单机、集群、哨兵、主从等所有部署模式。

        其中,分布式锁是 Redisson 最核心的功能,它通过 Redis 的 Hash 数据结构和 Lua 脚本保证锁的原子性,同时提供 “看门狗(Watch Dog)” 机制解决锁续期问题。

Redisson 使用

        Redisson 提供多种分布式锁类型,可重入锁(RLock) 是最常用的基础锁,其他锁(公平锁、读写锁等)均基于其扩展。

1. 可重入锁(RLock)

        可重入锁支持同一线程多次获取锁(解决 “自己锁自己” 的问题),且内置 Watch Dog 自动续期 机制,避免业务未执行完锁已过期。

(1)核心 API

方法功能说明
lock()阻塞获取锁,默认启用 Watch Dog(leaseTime=-1),直到获取成功
lock(long leaseTime, TimeUnit unit)带过期时间的锁,leaseTime 后自动释放,禁用 Watch Dog
tryLock()非阻塞获取锁,立即返回 true(成功)或 false(失败)
tryLock(long waitTime, long leaseTime, TimeUnit unit)带等待时间的非阻塞锁:
waitTime:获取锁的最大等待时间(超时返回 false
leaseTime:锁的过期时间(0 或 -1 启用 Watch Dog)
unlock()释放锁,必须在 finally 中调用,避免死锁
isHeldByCurrentThread()判断当前线程是否持有锁,避免释放他人锁

(2)代码示例

        生产环境中优先使用 tryLock 避免无限阻塞,同时在 finally 中安全释放锁:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class OrderService {

    @Autowired
    private RedissonClient redissonClient;

    // 分布式锁的 Key(需保证业务唯一性,如“订单创建_用户ID”)
    private static final String LOCK_KEY = "order:create:%s";

    /**
     * 创建订单(分布式锁保护)
     * @param userId 用户ID(用于生成唯一锁Key)
     */
    public void createOrder(Long userId) {
        RLock lock = null;
        try {
            // 1. 生成唯一锁Key(按用户ID区分,避免锁冲突)
            String lockKey = String.format(LOCK_KEY, userId);
            // 2. 获取可重入锁
            lock = redissonClient.getLock(lockKey);

            // 3. 尝试获取锁:等待3秒,持有5秒(若5秒内未释放,锁自动过期)
            boolean isLocked = lock.tryLock(3, 5, TimeUnit.SECONDS);
            if (!isLocked) {
                // 4. 获取锁失败,返回友好提示
                throw new RuntimeException("当前操作过频繁,请稍后再试");
            }

            // 5. 获取锁成功,执行核心业务(如创建订单、扣减库存)
            System.out.println("获取锁成功,执行订单创建逻辑...");
            // 模拟业务耗时(如2秒)
            TimeUnit.SECONDS.sleep(2);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("订单创建失败");
        } finally {
            // 6. 安全释放锁:仅释放当前线程持有的锁
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
                System.out.println("锁释放成功");
            }
        }
    }
}

2. Watch Dog 自动续期

        当 leaseTime 未设置(或设为 -1)时,Redisson 会启动 Watch Dog 机制,解决 “业务未执行完锁已过期” 的问题:

  • 原理:Watch Dog 是一个后台线程,在锁快过期时(默认剩余 10 秒)自动将锁的过期时间延长至 30 秒;
  • 默认配置:锁默认过期时间 30 秒,续期间隔 10 秒;
  • 禁用场景:若手动设置 leaseTime(如 5 秒),Watch Dog 会失效,锁会在 leaseTime 后强制释放,需确保 leaseTime 大于业务执行时间。

3. 其他常用锁类型

        Redisson 提供多种锁类型,满足不同分布式场景需求:

(1)公平锁(RedissonFairLock)

        按请求顺序获取锁,避免 “饥饿问题”(某些线程长期获取不到锁),适用于对公平性要求高的场景(如秒杀排队)。使用示例

// 获取公平锁(替换 getLock 为 getFairLock)
RLock fairLock = redissonClient.getFairLock("order:create:1001");
try {
    if (fairLock.tryLock(3, 5, TimeUnit.SECONDS)) {
        // 执行公平锁保护的业务逻辑
    }
} finally {
    if (fairLock != null && fairLock.isHeldByCurrentThread()) {
        fairLock.unlock();
    }
}

(2)读写锁(RReadWriteLock)

        读锁共享,写锁互斥,适用于 “读多写少” 的场景(如商品详情查询、库存更新),提高并发效率:

  • 读锁(共享):多个线程可同时获取读锁,互不阻塞;
  • 写锁(互斥):仅一个线程可获取写锁,且写锁与读锁互斥。

使用示例

// 获取读写锁
RReadWriteLock rwLock = redissonClient.getReadWriteLock("product:info:1001");

// 1. 读操作(获取读锁)
RLock readLock = rwLock.readLock();
try {
    readLock.lock(5, TimeUnit.SECONDS);
    System.out.println("获取读锁,查询商品详情..."); // 多个线程可同时执行
} finally {
    readLock.unlock();
}

// 2. 写操作(获取写锁)
RLock writeLock = rwLock.writeLock();
try {
    writeLock.lock(5, TimeUnit.SECONDS);
    System.out.println("获取写锁,更新商品库存..."); // 仅一个线程执行
} finally {
    writeLock.unlock();
}

(3)联锁(RMultiLock)

        需同时获取多个锁,全部获取成功才继续执行,适用于 “多资源协同锁定” 场景(如转账需同时锁定付款方和收款方账户)。使用示例

// 1. 获取两个独立的锁
RLock lock1 = redissonClient.getLock("account:lock:1001"); // 付款方账户锁
RLock lock2 = redissonClient.getLock("account:lock:1002"); // 收款方账户锁

// 2. 创建联锁(需全部锁获取成功才生效)
RMultiLock multiLock = new RedissonMultiLock(lock1, lock2);

try {
    // 3. 尝试获取联锁(等待3秒,持有5秒)
    if (multiLock.tryLock(3, 5, TimeUnit.SECONDS)) {
        System.out.println("获取所有锁成功,执行转账逻辑...");
    }
} finally {
    if (multiLock != null && multiLock.isHeldByCurrentThread()) {
        multiLock.unlock();
    }
}

(4)红锁(RRedLock)

        在多个独立 Redis 节点(非集群,避免集群脑裂)上获取锁,超过半数节点获取成功即视为锁获取成功,适用于对锁可用性要求极高的场景(如金融交易),避免单点 Redis 故障导致锁失效。使用示例

// 1. 在3个独立Redis节点上创建锁
RLock lock1 = redissonClient.getLock("order:pay:1001"); // 节点1
RLock lock2 = redissonClient.getLock("order:pay:1001"); // 节点2(需单独配置RedissonClient)
RLock lock3 = redissonClient.getLock("order:pay:1001"); // 节点3

// 2. 创建红锁(超过半数节点成功即生效)
RRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

try {
    if (redLock.tryLock(3, 5, TimeUnit.SECONDS)) {
        System.out.println("红锁获取成功,执行支付逻辑...");
    }
} finally {
    if (redLock != null && redLock.isHeldByCurrentThread()) {
        redLock.unlock();
    }
}

Redisson 示例

        请参考项目地址中 springboot-lock/springboot-distributed-lock 模块代码。

Redisson 注意

1. 锁 Key 设计:保证唯一性

        锁 Key 需与业务场景强绑定,避免不同业务共用一个 Key 导致锁冲突。例如:

  • 订单创建:order:create:{userId}(按用户区分,避免同一用户并发创建订单);
  • 库存扣减:stock:deduct:{productId}(按商品区分,避免不同商品库存锁冲突)。

2. 安全释放锁:避免释放他人锁

  • 必须在 finally 中释放锁,确保业务异常时锁仍能释放;
  • 释放前通过 isHeldByCurrentThread() 判断当前线程是否持有锁,避免释放其他线程的锁(如线程 A 超时未释放,锁被自动过期,线程 B 获取锁后,线程 A 恢复执行释放了线程 B 的锁)。

3. 锁粒度:尽量减小锁范围

        避免将整个业务流程加锁,仅对 “共享资源修改” 的核心逻辑加锁,减少锁持有时间,提高并发效率。

4. Redis 高可用:避免锁服务单点故障

        分布式锁依赖 Redis,需确保 Redis 高可用:

  • 开发环境:单机 Redis(带持久化,避免重启丢失锁);
  • 生产环境:Redis 集群(3 主 3 从)或哨兵模式,避免单点 Redis 宕机导致锁不可用。

5. 序列化配置:避免特殊字符问题

        Redisson 默认使用 JDK 序列化,可能导致 Redis 中存储的 Key/Value 包含特殊字符(如 \xAC\xED),建议改为 Jackson 序列化,提高可读性。

Redisson 对比

特性原生 Redis 锁(setNx+expire)Redisson 分布式锁
重入性不支持(需手动维护线程标识和重入次数)原生支持(RLock)
自动续期不支持(需手动定时任务续期)支持(Watch Dog 机制)
锁释放安全可能释放他人锁(需手动判断线程标识)自动判断当前线程,安全释放
锁类型仅基础互斥锁支持公平锁、读写锁、联锁、红锁等
易用性需手动封装(原子操作、异常处理)开箱即用,API 简洁

        显然,Redisson 解决了原生 Redis 锁的所有痛点,是 Spring Boot 3 分布式系统中实现分布式锁的最优选择

Redisson 总结

        在生产环境中,只需关注 “锁 Key 设计”“锁粒度控制”“Redis 高可用” 三个核心点,即可安全、高效地使用 Redisson 分布式锁解决并发问题。Redisson 为 Spring Boot 3 提供了 “开箱即用” 的分布式锁解决方案,核心优势包括:

  1. 支持多种锁类型(可重入锁、公平锁、读写锁等),满足不同业务场景;
  2. 内置 Watch Dog 自动续期,避免业务未完成锁过期;
  3. 封装原子操作和异常处理,简化开发;
  4. 支持 Redis 所有部署模式,保证高可用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值