如何用SETNX实现分布式锁
基于Redis单线程特性实现分布式锁,通过SETNX命令确保多个客户端中只有一个能成功获取锁。SETNX(SET if Not eXists)命令在键不存在时设置值,成功返回1,失败返回0。
获取锁成功后需要设置过期时间,防止客户端崩溃导致锁无法释放。释放锁时需先验证持有权,确认无误后通过DEL命令删除。
public class RedisDistributedLock {private final JedisPool jedisPool;public RedisDistributedLock(JedisPool jedisPool) {this.jedisPool = jedisPool;}public boolean tryLock(String lockKey, String requestId, int expireTime) {try (Jedis jedis = jedisPool.getResource()) {String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);return "OK".equals(result);}}public boolean unlock(String lockKey, String requestId) {try (Jedis jedis = jedisPool.getResource()) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));return Long.parseLong(result.toString()) == 1L;}}
}
实现说明
tryLock方法通过set命令的NX/PX参数实现原子性加锁和过期时间设置unlock方法通过Lua脚本确保释放锁时的原子性校验- SETNX不支持直接设置超时,可通过两种方式实现:
- 组合命令:
SETNX key value+EXPIRE key 10 - 新版Redis支持:
SET key value EX 10 NX
- 组合命令:
优缺点分析
优点:
- 实现简单直观
- 性能优异,利用Redis单线程特性保证原子性
缺点:
- 不支持锁续期
- 未设置超时可能导致死锁
- 高并发场景存在竞争
- 原生不支持可重入,可通过Redisson实现
对于更完善的分布式锁实现,建议采用Redisson框架。
