现象

接口偶发卡死,请求一直等待不返回。

排查后发现某个 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,
避免手写锁带来的边界问题。