1. 分布式锁的核心需求与挑战
在分布式系统中,当多个服务实例需要互斥访问共享资源时,传统的单机锁机制就失效了。比如电商系统中的库存扣减、秒杀活动中的商品抢购,都需要确保同一时间只有一个请求能执行业务逻辑。这就是分布式锁要解决的核心问题——跨进程的互斥访问控制。
Redis实现分布式锁的优势在于:
- 高性能:基于内存操作,响应时间在毫秒级
- 原子性:通过Lua脚本或SETNX命令保证操作的不可分割性
- 可重入:通过线程标识支持同一线程多次获取锁
- 自动过期:避免死锁,通过TTL自动释放
但实现一个健壮的Redis分布式锁需要解决几个关键问题:
- 原子性获取锁与设置过期时间
- 避免误删其他客户端的锁
- 锁续期机制处理长耗时任务
- 集群环境下的可靠性保证
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础实现方案与原理
2.1 SETNX + EXPIRE 基础版
最基础的实现方式是组合使用SETNX和EXPIRE命令:
java复制// 获取锁
Boolean locked = redisTemplate.opsForValue().setIfAbsent("lock_key", "1");
if(locked) {
redisTemplate.expire("lock_key", 30, TimeUnit.SECONDS);
try {
// 执行业务逻辑
} finally {
redisTemplate.delete("lock_key");
}
}
但这个方案存在严重缺陷——SETNX和EXPIRE不是原子操作,如果在设置过期时间前进程崩溃,会导致锁永远无法释放。生产环境绝对不能用这种实现。
2.2 Redis 2.6+ 的原子性方案
Redis 2.6版本后扩展了SET命令的参数,支持原子化操作:
java复制String result = redisTemplate.opsForValue().set(
"lock_key",
"client1",
Duration.ofSeconds(30),
RedisStringCommands.SetOption.SET_IF_ABSENT
);
这个命令等价于:
code复制SET lock_key client1 NX EX 30
NX表示只有key不存在时才设置,EX设置过期时间,整个操作是原子的。这才是正确的实现基础。
3. 生产级实现方案
3.1 防止误删锁机制
基础方案存在锁被其他客户端误删的风险。比如:
- 客户端A获取锁,设置30秒过期
- 客户端A执行业务逻辑耗时35秒,锁已自动释放
- 客户端B获取到锁
- 客户端A执行完删除锁,此时删除的是B的锁
解决方案是在删除前验证锁的值是否是自己设置的:
java复制String clientId = UUID.randomUUID().toString();
try {
// 获取锁
Boolean locked = redisTemplate.opsForValue().setIfAbsent(
"lock_key",
clientId,
Duration.ofSeconds(30)
);
if(locked) {
// 执行业务逻辑
}
} finally {
// 只有锁的值是自己设置的才删除
String value = redisTemplate.opsForValue().get("lock_key");
if(clientId.equals(value)) {
