现象
接口偶发卡死,请求一直等待不返回。
排查后发现某个 key 长时间存在,业务线程始终获取不到锁。
原始实现
使用 setnx 实现分布式锁:
Boolean lock = redisTemplate.opsForValue()
.setIfAbsent("order_lock", "1");
if (Boolean.TRUE.equals(lock)) {
try {
// 执行业务
} finally {
redisTemplate.delete("order_lock");
}
}
问题原因
当服务在执行过程中异常退出(例如进程重启),
finally 块没有执行,锁未删除。
由于没有设置过期时间,key 会一直存在。
后续请求全部获取锁失败,形成类似“死锁”的情况。
调整方案
增加过期时间:
Boolean lock = redisTemplate.opsForValue()
.setIfAbsent("order_lock", "1", 30, TimeUnit.SECONDS);
避免锁永久存在。
进一步问题
如果业务执行超过 30 秒,
锁提前过期,其他线程可能获取锁,造成并发执行。
改进方式
使用唯一标识作为 value:
String requestId = UUID.randomUUID().toString();
Boolean lock = redisTemplate.opsForValue()
.setIfAbsent("order_lock", requestId, 30, TimeUnit.SECONDS);
释放锁时校验 value:
String value = redisTemplate.opsForValue().get("order_lock");
if (requestId.equals(value)) {
redisTemplate.delete("order_lock");
}
避免误删他人锁。
处理结果
后续统一改为使用 Redisson 的 RLock,
避免手写锁带来的边界问题。