Redis 分布式锁是分布式系统中解决并发安全 的核心方案,生产环境绝对不能用简单的 SETNX ,必须满足:加锁原子性、防死锁、防误删、可重入、高可用。
我会给你可直接落地生产的 Java 实现(Redisson 最佳实践) + 核心原理 + 避坑指南。
一、生产级 Redis 分布式锁核心要求
- 互斥性:同一时间只有一个客户端持有锁
- 防死锁:客户端宕机也能自动释放锁(必须设置过期时间)
- 原子性:加锁 + 设置过期必须是原子操作
- 防误删:只能删除自己加的锁,不能删除别人的
- 可重入:同一个线程/客户端可重复加锁(避免死锁)
- 高可用:Redis 集群/主从下依然可靠
- 容错性:锁等待、重试、超时机制
二、绝对不要用的错误实现
以下写法生产环境必出问题,严禁使用:
java
// 错误1:加锁和过期时间非原子,客户端宕机直接死锁
redis.setnx(key, value);
redis.expire(key, 30);
// 错误2:没有唯一标识,会误删别人的锁
redis.del(key);
// 错误3:判断和删除非原子,并发下依然误删
if (redis.get(key).equals(value)) {
redis.del(key); // 这里锁已过期,会删别人的锁
}
三、生产标准实现:基于 Redisson(推荐)
Redisson 是 Redis 官方推荐的分布式锁框架,完美实现了所有生产特性:
- 原子加锁/解锁
- 锁自动续期(看门狗机制)
- 可重入锁
- 公平锁/读写锁
- 红锁(解决 Redis 主从切换锁丢失问题)
- 支持集群、哨兵、单机模式
1. 依赖引入(Maven)
xml
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.29.0</version> <!-- 稳定版 -->
</dependency>
2. Redisson 配置(application.yml)
yaml
spring:
redis:
host: 127.0.0.1
port: 6379
password: your-password
database: 0
3. 生产可用工具类
java
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* 生产级 Redis 分布式锁工具类(基于 Redisson)
*/
@Component
public class RedisDistributedLock {
@Resource
private RedissonClient redissonClient;
/**
* 加锁
* @param lockKey 锁key
* @param waitTime 等待获取锁的时间
* @param leaseTime 锁自动释放时间(不传启用看门狗自动续期)
* @return 是否加锁成功
*/
public boolean lock(String lockKey, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁,最多等待waitTime,上锁后leaseTime秒自动解锁
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 解锁(仅自己的锁可解)
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
// 校验锁归属 + 解锁,原子操作
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
4. 业务代码使用示例
java
@Service
public class OrderService {
@Resource
private RedisDistributedLock distributedLock;
public void createOrder(Long userId) {
String lockKey = "order:lock:" + userId;
// 等待3秒,锁10秒自动释放
boolean lockSuccess = distributedLock.lock(lockKey, 3, 10);
if (!lockSuccess) {
throw new RuntimeException("请求太频繁,请稍后再试");
}
try {
// 核心业务逻辑(扣库存、创建订单...)
System.out.println("获取锁成功,执行业务");
} finally {
// 必须在 finally 中解锁
distributedLock.unlock(lockKey);
}
}
}
四、核心生产特性详解
1. 看门狗自动续期(避免业务未执行完锁过期)
- 如果不指定 leaseTime ,Redisson 会启用看门狗
- 默认每 30s 检查一次,只要线程没释放,就自动延长锁时间
- 彻底解决:业务执行时间 > 锁过期时间导致的锁失效问题
2. 防误删原理
- 每个锁携带唯一客户端ID + 线程ID
- 解锁时校验:
只有自己的锁才能解锁 - 解锁是 Lua 脚本原子操作,无并发安全问题
3. 可重入锁原理
- 锁内部维护计数器,同一线程重复加锁计数+1,解锁计数-1
- 计数为0时真正释放锁
- 避免同一线程重复加锁导致死锁
五、高可用终极方案:Redlock 红锁
适用场景
金融、支付等绝对不允许锁丢失的核心业务(解决 Redis 主从切换锁丢失问题)
原理
- 向N 个独立 Redis 节点(无主从)申请锁
- 超过半数节点加锁成功,才算加锁成功
- 解锁时向所有节点解锁
实现
java
// 红锁使用(多独立Redis节点)
RLock lock1 = redissonClient1.getLock(key);
RLock lock2 = redissonClient2.getLock(key);
RLock lock3 = redissonClient3.getLock(key);
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
redLock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
六、生产环境最佳实践
- 锁粒度尽可能小:只锁核心资源,提升并发性能
- 必须设置等待时间:避免无限阻塞
- 解锁必须放在 finally:保证无论如何都释放锁
- 核心业务用红锁:普通业务用普通分布式锁
- 监控锁竞争:添加日志、监控,排查锁等待、死锁问题
- 避免长业务持有锁:锁内只做原子操作,不做远程调用/慢查询
总结
- 生产环境必须用 Redisson,不要手写 Redis 锁
- 核心保障:原子加锁、唯一标识、Lua 解锁、看门狗续期、可重入
- 高可用:普通业务用普通锁,核心业务用 Redlock 红锁
- 代码可直接复制到生产环境使用,无坑、稳定可靠