1. Redis分布式锁演进历程
在分布式系统中,协调多个服务实例对共享资源的访问是一个经典难题。Redis分布式锁因其简单高效的特性,成为解决这一问题的首选方案。让我们从最基础的实现开始,逐步剖析其演进过程。
1.1 基础版:SETNX + EXPIRE(已废弃方案)
早期开发者最直观的实现方式是组合使用SETNX和EXPIRE命令:
bash复制SETNX lock:order 1 # 尝试获取锁
EXPIRE lock:order 30 # 设置过期时间
这个方案存在三个致命缺陷:
-
原子性问题:两个命令非原子执行,若在SETNX后客户端崩溃,EXPIRE不会执行,导致锁永远无法释放。我在实际项目中就遇到过因服务器突然宕机,导致订单系统锁死长达数小时的事故。
-
无持有者标识:锁的value只是简单的"1",任何客户端都能执行DEL命令释放锁。曾有一次线上故障,因服务A持有的锁被服务B误删,导致重复业务处理,造成数据不一致。
-
不可重入:同一线程多次获取锁会导致死锁。比如递归调用场景下,内层方法尝试获取已持有的锁时会被阻塞。
重要提示:这种实现方案目前已被业界废弃,仅作为反面教材用于教学。新项目绝对不要采用这种方式。
1.2 原子SET方案(当前基础标准)
Redis 2.6.12版本后,SET命令支持NX和PX选项,实现了原子化的锁获取:
bash复制SET lock:order client_001 NX PX 30000
这个单条命令解决了基础版的所有问题:
- 原子性保障:获取锁和设置过期时间在一个原子操作中完成
- 持有者标识:value使用客户端唯一ID(如UUID+线程ID)
- 自动释放:30秒后自动过期,避免死锁
参数说明:
NX:等效于SETNX,仅当key不存在时设置PX 30000:设置毫秒级过期时间(30秒)client_001:建议使用客户端标识+线程ID的组合
但实际使用中仍发现两个问题:
- 锁续期难题:业务执行时间超过30秒时,锁会提前释放
- 释放风险:需要额外逻辑确保只有持有者能释放锁
java复制// 典型的安全释放锁实现
if (redis.get("lock:order").equals(clientId)) {
redis.del("lock:order")
}
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Redisson生产级实现解析
2.1 核心架构设计
Redisson的分布式锁实现包含以下几个关键组件:
- 锁状态存储:使用Redis Hash结构存储锁信息
- key:锁名称(如"order_lo
