分布式锁,在分布式系统里是个绕不开的话题——多个节点同时访问共享资源,总得有个协调机制。Redis凭借高性能和原子操作,成了实现分布式锁的主流选择。下面从原理层面拆解Redis分布式锁的核心逻辑,再详细分析三种常见实现方式的代码、优缺点,以及生产环境里要特别注意的地方。 一、Redis分布式锁核心原
分布式锁,在分布式系统里是个绕不开的话题——多个节点同时访问共享资源,总得有个协调机制。Redis凭借高性能和原子操作,成了实现分布式锁的主流选择。下面从原理层面拆解Redis分布式锁的核心逻辑,再详细分析三种常见实现方式的代码、优缺点,以及生产环境里要特别注意的地方。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
一个可靠的分布式锁,得满足以下几个特性:
Redis依靠下面这几个核心命令来支撑分布式锁:
| 命令/特性 | 作用 |
|---|---|
SET key value NX EX t |
原子执行“不存在则设置(NX)+ 过期时间(EX)”,避免加锁与设超时拆成两步操作 |
DEL key |
删除锁(释放锁),需要配合校验锁归属,否则可能误删别人的锁 |
| Lua脚本 | 将“校验锁归属+释放锁”封装成一个原子操作,解决释放锁时的并发安全问题 |
| Redisson(客户端) | 基于Redis封装了可重入、自动续期、公平锁等高级特性,大大简化了锁的使用 |
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 示例:扣减库存(基础分布式锁实现)
*/
private void order(){
// 1. 生成唯一锁值(用于校验锁归属,避免误删)
String lockValue = UUID.randomUUID().toString();
// 2. 加锁:SETNX + 过期时间(原子操作),30秒自动释放
Boolean locked = stringRedisTemplate.opsForValue()
.setIfAbsent("product:1001:lock", lockValue, 30, TimeUnit.SECONDS);
try {
// 3. 加锁成功则执行业务逻辑(扣减库存)
if (locked) {
Integer count = (Integer) stringRedisTemplate.opsForHash().get("product:1001",
"number");
if (count > 0) {
stringRedisTemplate.opsForHash().put("product:1001", "number", count - 1);
}
}
} finally {
// 4. 释放锁:先校验锁归属,再删除(非原子操作)
if (lockValue.equals(stringRedisTemplate.opsForValue().get("product:1001:lock"))) {
stringRedisTemplate.delete("product:1001:lock");
}
}
}
setIfAbsent(底层就是SET NX EX)实现原子加锁,同时设置30秒超时,万一客户端宕机,锁也会自动释放,避免死锁;lockValue,释放锁前校验值是否匹配,防止误删其他客户端的锁;delete就会把别人的锁给删掉;stringRedisTemplate.opsForHash().get()返回的是Object,强转Integer很可能出现类型异常(所以一定要先判空再加类型校验)。@Resource
private StringRedisTemplate stringRedisTemplate;
private static final String LOCK_KEY = "product:1001:lock";
private static final String STOCK_KEY = "product:1001:number";
private static final long LOCK_TIMEOUT = 30; // 锁超时时间(秒)
private static final long SLEEP_TIME = 100; // 重试间隔(毫秒)
private void order() {
String lockValue = UUID.randomUUID().toString();
try {
// 1. 尝试获取锁(原子加锁)
Boolean locked = tryAcquireLock(lockValue);
if (!locked) {
// 加锁失败可重试/返回失败(示例直接返回,实际可加循环重试)
return;
}
// 2. 执行业务:获取并扣减库存(简化为String结构,避免Hash类型转换问题)
String stockStr = stringRedisTemplate.opsForValue().get(STOCK_KEY);
if (stockStr == null || Integer.parseInt(stockStr) <= 0) {
return;
}
stringRedisTemplate.opsForValue().set(STOCK_KEY, String.valueOf(Integer.parseInt(stockStr) - 1));
} finally {
// 3. 释放锁:Lua脚本封装“校验+删除”,保证原子性
releaseLock(lockValue);
}
}
/**
* 原子加锁:SET NX EX
*/
private Boolean tryAcquireLock(String lockValue) {
return stringRedisTemplate.opsForValue()
.setIfAbsent(LOCK_KEY, lockValue, LOCK_TIMEOUT, TimeUnit.SECONDS);
}
/**
* 原子释放锁:Lua脚本
*/
private void releaseLock(String lockValue) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
stringRedisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Arrays.asList(LOCK_KEY),
lockValue
);
}
tryAcquireLock和releaseLock方法,复用性更好。LOCK_TIMEOUT(30秒),锁会提前过期,导致并发安全问题;Integer.parseInt(stockStr)没有做异常捕获,如果库存值不是数字,会抛出运行时异常;Redisson是Redis官方推荐的Ja va客户端,它内置了分布式锁的完整实现,解决了手动实现时遇到的诸多痛点。
@Resource
private RedissonClient redissonClient;
@Resource
private StringRedisTemplate stringRedisTemplate;
private void order() {
// 1. 获取分布式锁对象(可重入锁)
RLock lock = redissonClient.getLock("product:1001:lock");
try {
// 2. 加锁:最多等待10秒,锁30秒后自动释放;获取锁成功则执行业务
if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
try {
// 3. 扣减库存业务逻辑
Integer count = (Integer) stringRedisTemplate.opsForHash().get("product:1001",
"number");
if (count != null && count > 0) {
stringRedisTemplate.opsForHash().put("product:1001", "number", count - 1);
}
} finally {
// 4. 手动释放锁(若业务执行完未超时,主动释放)
lock.unlock();
}
}
} catch (InterruptedException e) {
// 5. 中断异常处理,恢复线程中断状态
Thread.currentThread().interrupt();
}
}
tryLock不会导致死锁;tryLock(waitTime, leaseTime, unit)支持“最大等待时间”,加锁失败时会阻塞等待,直到超时或者获取到锁;finally块中执行unlock(),但最好先判断lock.isHeldByCurrentThread(),避免未持有锁时执行解锁抛出异常;tryLock会抛出InterruptedException,需要捕获并恢复线程的中断状态,避免线程状态异常;| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 方式1(基础版) | 代码简单、无额外依赖 | 释放锁非原子、无续期、易误删锁 | 测试环境、低并发非核心业务 |
| 方式2(Lua版) | 释放锁原子化、代码结构清晰 | 无续期、重试逻辑需手动实现、单点风险 | 中小并发、核心逻辑简单场景 |
| 方式3(Redisson) | 自动续期、可重入、集群适配 | 引入Redisson依赖、配置稍复杂 | 生产环境、高并发核心业务 |
RedissonNode或ClusterServersConfig,避免主从切换导致锁丢失;Redis分布式锁的核心说白了就是原子加锁 + 安全释放 + 超时兜底:
生产环境中,除非有特殊的定制需求,否则优先基于Redisson来实现分布式锁,既保证可靠性,又降低开发和维护成本。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述