Redis分布式锁实战,避免并发冲突与死锁,确保数据一致性与系统高可用性的高效实现方案

文章导读
高效实现方案的核心是使用Redlock算法结合Lua脚本,确保原子性和自动续期,避免死锁。以下是完整Java代码示例:public class RedisDistributedLock { private static final String LOCK_SCRIPT = "if redis.call('exists', KEYS[1]) == 0 or redis.call('hexist
📋 目录
  1. 方案一:基础SetNX实现
  2. 方案二:Redlock算法
  3. 方案三:自动续期机制
  4. 方案四:Lua脚本原子性
  5. 方案五:多节点一致性
A A

高效实现方案的核心是使用Redlock算法结合Lua脚本,确保原子性和自动续期,避免死锁。以下是完整Java代码示例:
public class RedisDistributedLock {
private static final String LOCK_SCRIPT = "if redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 1 then " +
"redis.call('hincrby', KEYS[1], ARGV[1], 1); " +
"redis.call('expire', KEYS[1], ARGV[2]); " +
"return 1; " +
"else " +
"return 0; " +
"end;";
public boolean acquireLock(String lockKey, String threadId, long expireTime) {
return (Boolean) jedis.eval(LOCK_SCRIPT, Collections.singletonList(lockKey),
Arrays.asList(threadId, String.valueOf(expireTime)));
}
public boolean releaseLock(String lockKey, String threadId) {
String script = "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 then return nil end " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1) " +
"if counter > 0 then return 0 else redis.call('del', KEYS[1]) return 1 end;";
return (Long) jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(threadId)) == 1;
}
} 这套方案通过哈希表实现重入锁,支持自动续期和多节点Redlock部署,确保高可用。

方案一:基础SetNX实现

Redis分布式锁最简单的方式就是使用setnx命令。当且仅当key不存在时,setnx命令才能成功,如果给定的key已经存在,则setnx会返回0。setnx的含义是SET if Not eXists,会在设置key的同时设置一个过期时间,这样的话即使锁持有者后续发生宕机而无法删除锁key,锁也会因为过期时间到达而自动删除。

方案二:Redlock算法

Redlock是Redis官方推荐的分布式锁算法,解决了单点Redis的可靠性问题。算法步骤:1)获取当前时间戳;2)轮询N个Redis节点(建议5个),使用相同的key和随机value尝试获取锁,每次获取需要小于锁的TTL时间;3)计算整个获取锁的过程总耗时,如果总耗时小于TTL,且至少N/2+1个节点获取成功,则锁获取成功;4)如果获取成功,在业务执行完后,需要去所有节点释放锁,即使部分节点没有成功获取锁,也需要释放;5)如果获取失败,立即去所有节点释放锁,并返回失败。

Redis分布式锁实战,避免并发冲突与死锁,确保数据一致性与系统高可用性的高效实现方案

方案三:自动续期机制

为了防止业务处理时间过长导致锁过期,引入watchdog守护线程,每30s检查一次锁是否还存在,如果存在就调用expire命令延长锁的有效期。续期时间一般设置为锁过期时间的三分之一。同时,业务线程获取锁后启动watchdog,业务结束立即释放锁并停止watchdog,避免误释放他人锁。

Redis分布式锁实战,避免并发冲突与死锁,确保数据一致性与系统高可用性的高效实现方案

方案四:Lua脚本原子性

使用Lua脚本确保加锁和续期操作原子性,避免网络延迟导致的竞态条件。脚本内容:if redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2]) then return 1 end return 0。Lua脚本在Redis单线程执行,保证原子性,远优于多个SET命令组合。

方案五:多节点一致性

部署5个独立Redis节点,客户端同时向多数节点(3个)加锁,加锁成功后才认为锁有效。释放时向所有节点释放,即使某些节点未加锁成功也要释放。这确保了即使单个节点故障,锁仍能正常工作,提高系统高可用性。

Redis分布式锁实战,避免并发冲突与死锁,确保数据一致性与系统高可用性的高效实现方案

FAQ
Q: Redis分布式锁为什么会死锁?
A: 死锁通常因服务宕机前未释放锁且无过期时间引起,使用SET NX PX命令设置过期时间可避免。
Q: 如何处理锁续期?
A: 启动后台线程每隔锁TTL的1/3检查并续期,业务结束立即释放。
Q: Redlock适合所有场景吗?
A: 不适合时钟不同步或网络分区严重的场景,单机锁或ZooKeeper更合适。
Q: 如何实现重入锁?
A: 使用Hash存储线程ID和重入次数,支持同一线程多次加锁。