完整Redis分布式锁技术方案(基于Redisson)

完整Redis分布式锁技术方案(基于Redisson)

一、技术方案概述

1. 设计理念

基于Redisson实现高可靠分布式锁,支持单机/集群Redis自动适配 ,提供单点锁、联锁核心功能,通过静态工具类简化业务调用,同时保障锁的原子性、自动续期、超时控制等特性,适用于分布式系统中的并发场景(如库存扣减、订单创建、数据同步等)。

2. 核心优势

  • 自动适配Redis部署模式(单机/集群),无需手动修改配置;
  • 支持锁自动续期(leaseTime=-1时),避免业务未完成锁提前释放;
  • 提供联锁功能,支持多key原子性锁定(解决分布式场景下多资源并发竞争问题);
  • 完善的异常处理和线程中断支持,确保锁释放的安全性;
  • 基于Spring Boot自动配置,零侵入集成业务系统。

二、完整代码实现

1. 包结构规范

kotlin 复制代码
com.data.redis
├── DistributedLockTool.java       // 静态调用工具类
└── redisson
    ├── DistributedLocker.java      // 分布式锁核心接口
    ├── RedissonDistributedLocker.java  // Redisson实现类
    └── RedissonAutoConfiguration.java  // 自动配置类

2. 核心接口(DistributedLocker.java)

java 复制代码
package com.data.redis.redisson;

import java.util.concurrent.TimeUnit;

/**
 * 分布式锁接口
 * @author 2021/5/21
 */
public interface DistributedLocker {

    /**
     * 获取锁
     * @param lockKey 锁key(建议格式:业务模块:资源标识:唯一ID,如order:create:1001)
     * @param leaseTime 持有锁时间(单位:unit),-1表示自动续期
     * @param waitTime 申请锁超时时间(单位:unit)
     * @param unit 时间单位
     * @return true=获取成功,false=获取失败
     */
    boolean lock(String lockKey, long leaseTime, long waitTime, TimeUnit unit);

    /**
     * 获取联锁(多key原子锁)
     * @param lockKeys 锁key集合(需保证所有key都获取成功才持有锁,任意一个失败则全部失败)
     * @param leaseTime 持有锁时间,-1表示自动续期
     * @param waitTime 申请锁超时时间
     * @param unit 时间单位
     * @return true=获取成功,false=获取失败
     */
    boolean multiLock(Collection<String> lockKeys, long leaseTime, long waitTime, TimeUnit unit);

    /**
     * 释放锁(仅释放当前线程持有的锁)
     * @param lockKey 锁key
     */
    void unlock(String lockKey);

    /**
     * 释放联锁
     * @param lockKeys 锁key集合
     */
    void multiUnlock(Collection<String> lockKeys);
}

3. Redisson实现类(RedissonDistributedLocker.java)

java 复制代码
package com.data.redis.redisson;

import org.redisson.api.RedissonClient;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁实现(基于Redisson)
 * @author 2021/5/21
 */
public class RedissonDistributedLocker implements DistributedLocker {

    private final RedissonClient redissonClient;

    // 构造器注入RedissonClient
    public RedissonDistributedLocker(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public boolean lock(String lockKey, long leaseTime, long waitTime, TimeUnit unit) {
        try {
            // Redisson的tryLock支持超时等待、持有时间、自动续期(leaseTime=-1时)
            return redissonClient.getLock(lockKey).tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            // 恢复线程中断状态
            Thread.currentThread().interrupt();
            return false;
        }
    }

    @Override
    public boolean multiLock(Collection<String> lockKeys, long leaseTime, long waitTime, TimeUnit unit) {
        try {
            // 批量获取锁对象,构建联锁
            org.redisson.api.RLock[] rLocks = lockKeys.stream()
                    .map(redissonClient::getLock)
                    .toArray(org.redisson.api.RLock[]::new);
            // 联锁特性:所有锁都获取成功才返回true,任意一个失败则全部释放
            return redissonClient.getMultiLock(rLocks).tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }

    @Override
    public void unlock(String lockKey) {
        org.redisson.api.RLock lock = redissonClient.getLock(lockKey);
        // 仅释放当前线程持有的锁,避免释放其他线程的锁
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }

    @Override
    public void multiUnlock(Collection<String> lockKeys) {
        org.redisson.api.RLock[] rLocks = lockKeys.stream()
                .map(redissonClient::getLock)
                .toArray(org.redisson.api.RLock[]::new);
        redissonClient.getMultiLock(rLocks).unlock();
    }
}

4. 静态工具类(DistributedLockTool.java)

typescript 复制代码
package com.data.redis;

import com.data.redis.redisson.DistributedLocker;
import java.util.Collection;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁静态工具类(业务层直接调用入口)
 * @author 2021/5/21
 */
public class DistributedLockTool {

    // 由Spring自动注入DistributedLocker实现类
    private static DistributedLocker distributedLocker;

    // 提供setter方法,供Spring配置类注入
    public static void setDistributedLocker(DistributedLocker distributedLocker) {
        DistributedLockTool.distributedLocker = distributedLocker;
    }

    /**
     * 获取单点锁
     * @param lockKey 锁key
     * @param leaseTime 持有时间(-1=自动续期)
     * @param waitTime 申请超时时间
     * @param unit 时间单位
     * @return 是否获取成功
     */
    public static boolean lock(String lockKey, long leaseTime, long waitTime, TimeUnit unit) {
        checkLockerInit();
        return distributedLocker.lock(lockKey, leaseTime, waitTime, unit);
    }

    /**
     * 获取联锁(多key)
     * @param lockKeys 锁key集合
     * @param leaseTime 持有时间(-1=自动续期)
     * @param waitTime 申请超时时间
     * @param unit 时间单位
     * @return 是否获取成功
     */
    public static boolean multiLock(Collection<String> lockKeys, long leaseTime, long waitTime, TimeUnit unit) {
        checkLockerInit();
        return distributedLocker.multiLock(lockKeys, leaseTime, waitTime, unit);
    }

    /**
     * 释放单点锁
     * @param lockKey 锁key
     */
    public static void unlock(String lockKey) {
        checkLockerInit();
        distributedLocker.unlock(lockKey);
    }

    /**
     * 释放联锁
     * @param lockKeys 锁key集合
     */
    public static void multiUnlock(Collection<String> lockKeys) {
        checkLockerInit();
        distributedLocker.multiUnlock(lockKeys);
    }

    /**
     * 检查DistributedLocker是否已初始化(避免空指针)
     */
    private static void checkLockerInit() {
        if (distributedLocker == null) {
            throw new IllegalStateException("分布式锁组件未初始化,请检查Redisson配置是否正确");
        }
    }
}

5. 自动配置类(RedissonAutoConfiguration.java)

java 复制代码
package com.data.redis.redisson;

import com.data.redis.DistributedLockTool;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Redisson自动配置类(适配Spring Boot,支持单机/集群模式)
 * @author 2021/5/21
 */
@Configuration
@ConditionalOnClass(Config.class) // 存在Redisson的Config类时才生效
@EnableConfigurationProperties(RedisProperties.class) // 启用RedisProperties配置绑定
public class RedissonAutoConfiguration {

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(RedissonAutoConfiguration.class);

    private final RedisProperties redisProperties;

    // 注入Spring Boot默认的Redis配置(application.yml中的spring.redis配置)
    public RedissonAutoConfiguration(RedisProperties redisProperties) {
        this.redisProperties = redisProperties;
    }

    /**
     * 初始化RedissonClient(自动适配单机/集群模式)
     * @return RedissonClient实例
     */
    @Bean
    public RedissonClient redissonClient() {
        int timeout = 3000; // 默认连接超时时间:3秒
        if (redisProperties.getTimeout() != null) {
            // 转换为毫秒(RedisProperties的timeout单位是秒)
            timeout = redisProperties.getTimeout().getSeconds() > 0 ?
                    (int) redisProperties.getTimeout().toMillis() : timeout;
        }

        Config config = new Config();

        // 适配单机模式(默认)和集群模式
        if (redisProperties.getCluster() == null || redisProperties.getCluster().getNodes().isEmpty()) {
            logger.info("【Redisson】初始化单机模式配置,地址:{}:{}", redisProperties.getHost(), redisProperties.getPort());
            // 单机模式配置
            Config.SingleServerConfig singleServerConfig = config.useSingleServer()
                    .setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort())
                    .setTimeout(timeout) // 连接超时时间
                    .setConnectionPoolSize(64) // 连接池最大容量
                    .setConnectionMinimumIdleSize(10); // 连接池最小空闲连接数

            // 配置Redis密码(如果有)
            if (redisProperties.getPassword() != null && !redisProperties.getPassword().trim().isEmpty()) {
                singleServerConfig.setPassword(redisProperties.getPassword());
            }
        } else {
            logger.info("【Redisson】初始化集群模式配置,节点:{}", redisProperties.getCluster().getNodes());
            // 集群模式配置
            String[] nodeAddresses = redisProperties.getCluster().getNodes().stream()
                    .map(node -> "redis://" + node)
                    .toArray(String[]::new);

            Config.ClusterServersConfig clusterServersConfig = config.useClusterServers()
                    .addNodeAddress(nodeAddresses)
                    .setTimeout(timeout)
                    .setConnectTimeout(3000) // 集群节点连接超时时间
                    .setIdleConnectionTimeout(600000); // 空闲连接超时时间(10分钟)

            if (redisProperties.getPassword() != null && !redisProperties.getPassword().trim().isEmpty()) {
                clusterServersConfig.setPassword(redisProperties.getPassword());
            }
        }

        return Redisson.create(config);
    }

    /**
     * 初始化分布式锁实现类,并注入到静态工具类
     * @param redissonClient RedissonClient实例(由上面的@Bean方法提供)
     * @return DistributedLocker实现类
     */
    @Bean
    @ConditionalOnBean(RedissonClient.class) // 存在RedissonClient时才初始化
    public DistributedLocker distributedLocker(RedissonClient redissonClient) {
        RedissonDistributedLocker locker = new RedissonDistributedLocker(redissonClient);
        // 注入到静态工具类,供业务层直接调用
        DistributedLockTool.setDistributedLocker(locker);
        return locker;
    }
}

三、依赖配置(Maven/Gradle)

1. Maven(pom.xml)

xml 复制代码
<!-- Spring Boot Redis自动配置 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- Redisson核心依赖(兼容Spring Boot) -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.3</version> <!-- 推荐使用稳定版,与Redis版本兼容 -->
</dependency>

<!-- 若使用Spring Cloud,需确保与Spring Boot版本匹配 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>2023.0.4</version> <!-- 根据实际使用的Spring Cloud版本调整 -->
    <type>pom</type>
    <scope>import</scope>
</dependency>

2. Gradle(build.gradle)

java 复制代码
// Spring Boot Redis自动配置
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Redisson核心依赖
implementation 'org.redisson:redisson-spring-boot-starter:3.23.3'

// Spring Cloud依赖(根据实际版本调整)
implementation platform('org.springframework.cloud:spring-cloud-dependencies:2023.0.4')

四、使用示例(业务层调用)

1. 单点锁使用场景(如订单创建防重复提交)

kotlin 复制代码
import com.data.redis.DistributedLockTool;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;

@Service
public class OrderService {

    /**
     * 创建订单(防重复提交)
     * @param userId 用户ID
     * @param orderNo 订单号(唯一标识)
     * @return 订单创建结果
     */
    public String createOrder(Long userId, String orderNo) {
        // 1. 设计锁key:业务模块:资源标识:唯一ID(确保锁的粒度合理)
        String lockKey = "order:create:" + orderNo;

        try {
            // 2. 获取锁:申请超时3秒,持有锁5秒(若业务耗时较长,设为-1自动续期)
            boolean lockSuccess = DistributedLockTool.lock(lockKey, 5, 3, TimeUnit.SECONDS);
            if (!lockSuccess) {
                // 3. 锁获取失败(如超时),返回友好提示
                return "操作过于频繁,请稍后再试";
            }

            // 4. 锁获取成功,执行核心业务逻辑(如创建订单、扣减库存等)
            System.out.println("订单创建成功,orderNo:" + orderNo);
            return "订单创建成功";

        } catch (Exception e) {
            // 5. 业务异常处理
            logger.error("创建订单失败,orderNo:{}", orderNo, e);
            return "订单创建失败";
        } finally {
            // 6. 释放锁(必须在finally中执行,确保锁一定被释放)
            DistributedLockTool.unlock(lockKey);
        }
    }
}

2. 联锁使用场景(如转账时锁定转出/转入账户)

java 复制代码
import com.data.redis.DistributedLockTool;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class TransferService {

    /**
     * 账户转账(锁定转出和转入账户,避免并发问题)
     * @param fromAccount 转出账户
     * @param toAccount 转入账户
     * @param amount 转账金额
     * @return 转账结果
     */
    public String transfer(String fromAccount, String toAccount, BigDecimal amount) {
        // 1. 设计联锁key:按固定顺序排序(避免死锁)
        String lockKey1 = "account:lock:" + fromAccount;
        String lockKey2 = "account:lock:" + toAccount;
        List<String> lockKeys = Arrays.asList(lockKey1, lockKey2);

        try {
            // 2. 获取联锁:申请超时5秒,持有锁10秒(自动续期设为-1)
            boolean lockSuccess = DistributedLockTool.multiLock(lockKeys, 10, 5, TimeUnit.SECONDS);
            if (!lockSuccess) {
                return "转账繁忙,请稍后再试";
            }

            // 3. 执行转账业务逻辑(扣减转出账户金额,增加转入账户金额)
            System.out.println("转账成功:" + fromAccount + " -> " + toAccount + ",金额:" + amount);
            return "转账成功";

        } catch (Exception e) {
            logger.error("转账失败,from:{},to:{}", fromAccount, toAccount, e);
            return "转账失败";
        } finally {
            // 4. 释放联锁
            DistributedLockTool.multiUnlock(lockKeys);
        }
    }
}

五、关键配置说明(application.yml)

yaml 复制代码
spring:
  redis:
    # 单机模式配置
    host: 127.0.0.1
    port: 6379
    password: 123456 # Redis密码(无密码则省略)
    timeout: 3000 # 连接超时时间(秒)
    # 集群模式配置(单机模式时注释)
    # cluster:
    #   nodes:
    #     - 192.168.1.100:6379
    #     - 192.168.1.101:6379
    #     - 192.168.1.102:6379
  # Redisson额外配置(可选)
redisson:
  codec: org.redisson.codec.JsonJacksonCodec # 序列化方式(默认Jackson)
  threads: 4 # 业务线程池大小
  netty-threads: 8 # Netty线程池大小

六、注意事项(避坑指南)

  1. 锁key设计规范

必须保证锁key的唯一性和粒度合理性,建议格式:业务模块:资源类型:唯一标识(如inventory:deduct:1001),避免锁粒度过粗(导致并发低)或过细(导致锁过多)。

  1. 避免死锁

使用联锁时,必须按固定顺序排列锁key(如按字典序排序),避免两个线程同时持有部分锁,互相等待对方释放。

  1. 锁释放时机

必须在finally块中释放锁,确保即使业务异常,锁也能正常释放,避免死锁。

  1. 自动续期场景

当业务耗时不确定时(如大文件处理),将leaseTime设为-1,Redisson会自动续期锁(默认每30秒续期一次),避免锁提前释放。

  1. Redis集群可靠性

生产环境建议使用Redis集群(主从+哨兵或Redis Cluster),确保Redisson客户端能自动切换节点,避免单点故障导致锁失效。

  1. 并发量控制

连接池大小(connectionPoolSize)建议根据Redis性能和业务并发量调整(默认64),避免连接池耗尽导致锁获取超时。

七、完整组件清单(可直接复制集成)

包路径/类名 作用
com.data.redis.DistributedLockTool 静态调用入口,简化业务使用
com.data.redis.redisson.DistributedLocker 分布式锁核心接口,定义规范
com.data.redis.redisson.RedissonDistributedLocker 接口实现,基于Redisson封装
com.data.redis.redisson.RedissonAutoConfiguration 自动配置类,适配Spring Boot
pom.xml/build.gradle 依赖配置,引入Redisson和Redis
application.yml Redis和Redisson配置
相关推荐
程序媛青青2 小时前
spring boot 和 spring cloud 的区别
spring boot·后端·spring cloud
树下水月3 小时前
kafka的topic积压的问题汇总
分布式·kafka
m0_488777653 小时前
Redis三种服务架构
redis·架构·集群·哨兵
山南有清风3 小时前
开源对象存储项目一览
linux·分布式·对象存储·存储
豫狮恒3 小时前
OpenHarmony Flutter 分布式设备发现与组网:跨设备无感连接与动态组网方案
分布式·flutter·wpf·openharmony
菜鸟小九3 小时前
redis基础(数据结构)
数据结构·数据库·redis
周杰伦_Jay3 小时前
【JVM深度解析】运行时数据区+类加载+GC+调优实战(附参数示例)
java·jvm·spring boot·分布式·架构·java-ee
妮妮喔妮3 小时前
Kafka的死信队列
分布式·kafka
李白你好4 小时前
Redis 漏洞图形化利用工具
数据库·redis·缓存