Redis 分布式锁的正确实现:从 SETNX 到 Redisson
David Ng | 2026-08-26T23:02:06 | Java, Database
分析 Redis 分布式锁的三种实现方式及其缺陷,推荐使用 Redisson 的 RedLock 实现生产级分布式锁。
# Redis 分布式锁的正确实现 ## 方式一:简单 SETNX(有缺陷) ```java // 获取锁 Boolean locked = redis.opsForValue().setIfAbsent( "lock:order:" + orderId, requestId, 30, TimeUnit.SECONDS ); // 释放锁(存在竞态条件!) if (requestId.equals(redis.opsForValue().get(lockKey))) { redis.delete(lockKey); // GET 和 DELETE 不是原子操作 } ``` **问题**:释放锁的 GET + DELETE 不是原子的,可能删掉别人的锁。 ## 方式二:Lua 脚本原子释放 ```java private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then " + " return redis.call('del', KEYS[1]) " + "else " + " return 0 " + "end"; public boolean unlock(String lockKey, String requestId) { Long result = redis.execute( new DefaultRedisScript(UNLOCK_SCRIPT, Long.class), Collections.singletonList(lockKey), requestId ); return Long.valueOf(1).equals(result); } ``` **问题**:锁过期时间固定,业务执行超时后锁自动释放,依然不安全。 ## 方式三:Redisson(推荐) ```java @Configuration public class RedissonConfig { @Bean public RedissonClient redissonClient() { Config config = new Config(); config.useSingleServer() .setAddress("redis://localhost:6379") .setPassword("password"); return Redisson.create(config); } } @Service public class OrderService { @Autowired private RedissonClient redisson; public void createOrder(String orderId) { RLock lock = redisson.getLock("lock:order:" + orderId); try { // 等待 5 秒获取锁,自动续期 if (lock.tryLock(5, TimeUnit.SECONDS)) { try { doCreateOrder(orderId); } finally { lock.unlock(); } } else { throw new BusinessException("获取锁失败,请稍后重试"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } ``` ## Redisson 的看门狗机制 Redisson 的 `tryLock()` 会启动一个 watchdog 线程,每隔 10 秒(默认 leaseTime / 3)自动续期。业务执行完毕或线程挂掉后停止续期。 ## 三种方式对比 | 方式 | 原子性 | 自动续期 | 可重入 | 推荐度 | |------|--------|---------|--------|--------| | SETNX | 否 | 否 | 否 | 不推荐 | | Lua 脚本 | 是 | 否 | 否 | 一般 | | Redisson | 是 | 是 | 是 | 推荐 |