Redis分布式锁具体怎么实现的
Redis 分布式锁是利用 Redis 的原子操作特性,实现对共享资源的互斥访问,常见的实现方式主要基于 Redis 的 SET 命令(带 NX 和 PX 参数)或 Lua 脚本,保证加锁和解锁的原子性和安全性。下面我详细讲讲具体的实现步骤和常用方案:
🎯一、分布式锁的核心目标
-
互斥性:同一时刻只有一个客户端持有锁,防止资源冲突。
-
防死锁:锁应设置超时时间,防止因客户端崩溃而锁永远无法释放。
-
解锁安全:只有加锁的客户端能解锁,防止误解锁。
🧠二、最简单的分布式锁实现(基于 SET NX)
加锁示例
SET lock_key unique_value NX PX 30000
-
lock_key:锁的键名 -
unique_value:客户端唯一标识(一般是 UUID) -
NX:只在 key 不存在时设置,保证原子性加锁 -
PX 30000:设置锁过期时间,防止死锁,这里是 30 秒
返回值:
-
如果返回 OK,说明成功加锁。
-
如果返回 null,说明锁已被占用。
解锁示例(安全解锁)
因为锁可能会超时自动释放,或者被其他客户端加锁,所以解锁时要先确认自己是锁的持有者。
用 Lua 脚本保证判断和删除原子性:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
Java 伪代码:
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
redis.eval(script, Collections.singletonList("lock_key"), Collections.singletonList("unique_value"));
✅三、分布式锁完整流程示例
-
客户端生成唯一标识(UUID)。
-
客户端调用
SET lock_key UUID NX PX 30000尝试加锁。 -
如果成功,业务逻辑执行。
-
业务逻辑完成后,执行 Lua 脚本解锁,只删除自己持有的锁。
-
如果加锁失败,客户端可选择重试或放弃。
🎯四、分布式锁实现中常见问题与解决
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 1. 锁过期时间太短,业务没完成锁就释放 | 业务超时导致锁被误删 | 动态续期(watchdog机制),Redisson有实现 |
| 2. 客户端崩溃导致死锁 | 锁永远不释放 | 设置合理锁过期时间 |
| 3. 解锁误删别人锁 | 多客户端同时操作 | 使用唯一标识判断是否解锁 |
| 4. 单Redis节点故障 | 锁失效或失去互斥性 | 使用 Redlock 算法,多个 Redis 节点投票 |
✅五、Redlock 算法(高级分布式锁方案)
Redlock 是 Redis 官方推荐的分布式锁算法,适用于多节点 Redis 集群环境。
-
在多个独立 Redis 实例上分别尝试加锁(比如 5 个实例)。
-
加锁成功达到大多数(3 个)即视为加锁成功。
-
加锁失败则释放已获得的锁。
-
解锁时释放所有实例上的锁。
Redlock 能更好保证高可用和容错,但实现复杂,一般建议使用成熟客户端实现,比如 Redisson。
🎯六、Java 常用分布式锁客户端
-
Redisson:基于 Redis 实现的分布式工具集,内置分布式锁支持,支持可重入锁、公平锁、读写锁、信号量等,推荐使用。
-
Spring Data Redis:可以手动实现基于
SET NX PX的锁逻辑。 -
其他开源库:如
lettuce、jedis也可以自定义实现。
✅七、示例代码(Java + Jedis)
String lockKey = "lock:resource";
String uniqueId = UUID.randomUUID().toString();
int expireTime = 30000; // 30秒
// 加锁
String result = jedis.set(lockKey, uniqueId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
try {
// 业务处理
} finally {
// 释放锁,使用 Lua 脚本保证安全解锁
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
jedis.eval(luaScript, Collections.singletonList(lockKey), Collections.singletonList(uniqueId));
}
} else {
// 未获取到锁,重试或失败处理
}
更多推荐



所有评论(0)