当前位置: 首页 > news >正文

如何用Redis实现分布式锁?RedLock算法的核心思想?Redisson的看门狗机制原理?

一、Redis分布式锁基础实现

public class RedisDistributedLock {private JedisPool jedisPool;private String lockKey;private String clientId;private int expireTime = 30; // 默认30秒public boolean tryLock() {try (Jedis jedis = jedisPool.getResource()) {// NX表示不存在时设置,PX设置过期时间(毫秒)String result = jedis.set(lockKey, clientId, SetParams.setParams().nx().px(expireTime * 1000));return "OK".equals(result);}}public void unlock() {try (Jedis jedis = jedisPool.getResource()) {// 使用Lua脚本保证原子性String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"   return redis.call('del', KEYS[1]) " +"else " +"   return 0 " +"end";jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(clientId));}}
}

关键点:

  1. 使用SET NX PX命令保证原子性
  • 单命令原子性:Redis服务器单线程顺序执行命令
  • 仅包含SET操作:仅完成键值设置和过期时间配置
  • 无逻辑判断:仅判断键是否存在(NX特性)
  1. 客户端唯一标识(clientId)防止误删
  2. Lua脚本保证解锁操作的原子性
  • 多命令原子性:组合GET、DEL等命令的复合操作
  • 包含业务逻辑:实现"比较后删除"的CAS(Compare And Set)操作
  • 支持复杂流程:可包含条件判断、循环等逻辑

缺陷:
如果锁存在A redis节点,然后B是A的从库,服务先获取A节点的redis key锁,如果A网络波动的时候的时候,主从切换,B节点升级为主节点,这个时候另一个服务获取B节点的相同的redis key,这种情况就发生脑裂了。

二、RedLock算法核心思想

RedLock算法由Redis作者提出,主要解决单点故障问题:

  1. 多节点部署:使用5个(奇数)独立的Redis节点
  2. 顺序获取:客户端依次向所有节点申请锁
  3. 成功条件:获得超过半数的锁(3个)
  4. 耗时计算:总耗时应小于锁的TTL时间
  5. 失败释放:失败时需要释放所有已获得的锁

三、Redisson看门狗机制原理

public class RedissonWatchdogExample {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redisson = Redisson.create(config);RLock lock = redisson.getLock("myLock");try {// 默认30秒过期,看门狗自动续期lock.lock();// 业务逻辑执行时间可能超过30秒Thread.sleep(40000); } finally {lock.unlock();}}
}

集群防止脑裂

Redisson 实现分布式锁的核心机制和集群脑裂防护原理如下:

1. 基础锁实现原理
-- Redis 原子操作脚本
if redis.call('exists', KEYS[1]) == 0 thenredis.call('hset', KEYS[1], ARGV[2], 1)redis.call('pexpire', KEYS[1], ARGV[1])return nil
end
2. 集群模式防护机制
// RedissonMultiLock 集群锁实现
List<RLock> locks = new ArrayList<>();
locks.add(redissonClient1.getLock("lock1"));
locks.add(redissonClient2.getLock("lock2"));
RLock multiLock = new RedissonMultiLock(locks.toArray(new RLock[0]));

关键实现要点:

1. 多节点提交机制
  • 需要 N/2+1 个节点成功获取锁才算有效
  • 使用异步线程维持锁心跳(Watchdog 机制)
// Watchdog 线程实现(伪代码)
private void scheduleExpirationRenewal() {if (expirationRenewalMap.putIfAbsent(lockName, newTimeout) == null) {// 每 10 秒续期一次internalLockLeaseTime / 3 周期执行}
}
2. 脑裂防护策略
  • 同步延迟控制:主从同步超时时间 > 锁过期时间
  • 多数派原则:成功获取锁的节点数 > 集群半数节点
  • 故障转移阻断:当节点失联时自动启动锁失效倒计时
3. 异常处理机制
// 锁释放时的集群同步
public void unlock() {for (RLock lock : locks) {if (lock.isHeldByCurrentThread()) {lock.unlockAsync();}}
}

集群模式下重要参数配置建议:

# redisson.yaml
clusterServersConfig:nodeAddresses:- "redis://127.0.0.1:7000"- "redis://127.0.0.1:7001"scanInterval: 1000retryAttempts: 3retryInterval: 500slaveConnectionMinimumIdleSize: 8failedSlaveReconnectionInterval: 30000

该实现通过以下方式保证脑裂场景下的数据一致性:

  1. 使用 Raft 式多数派提交协议
  2. 网络分区时自动降级为只读模式
  3. 主节点切换后新主节点会等待旧主节点锁超时
  4. 客户端自动检测集群拓扑变化并重建连接
    看门狗机制关键点:
  5. 后台线程:每10秒检查锁状态
  6. 自动续期:当业务未完成时,将过期时间重置为30秒
  7. 客户端存活判断:只有客户端保持活跃才会续期
  8. 默认配置:lockWatchdogTimeout=30秒

四、完整方案对比

方案优点缺点
基础Redis锁实现简单单点故障风险
RedLock算法高可用性实现复杂、性能损耗
Redisson实现自动续期、可重入锁、多种锁类型需要维护客户端连接

实际建议:

  1. 单节点场景使用Redisson基础锁
  2. 高可用场景使用Redisson+Redis Cluster
  3. 极端可靠性需求使用RedLock算法

生产环境注意事项:

  1. 合理设置超时时间(业务平均耗时 * 2)
  2. 监控锁等待时间和获取次数
  3. 为不同业务使用不同的锁前缀
  4. 做好锁等待超时的异常处理

竞品分析

分布式锁实现方案对比及优劣势分析

一、ZooKeeper 实现方案
// 使用Curator框架示例
public class ZkDistributedLock {private CuratorFramework client;private InterProcessMutex lock;public boolean tryLock(String lockPath) throws Exception {lock = new InterProcessMutex(client, lockPath);return lock.acquire(3, TimeUnit.SECONDS); // 3秒获取超时}public void unlock() throws Exception {if (lock != null) {lock.release();}}
}

核心原理
通过创建临时顺序节点实现,获取锁的客户端会生成有序节点,只有序号最小的节点持有锁

优势

  1. 自动释放(会话失效时自动删除节点)
  2. 公平锁机制(顺序节点)
  3. 强一致性保证

劣势

  1. 写操作性能低于Redis
  2. 需要维护ZooKeeper集群
  3. 客户端实现相对复杂

二、Etcd 实现方案
// 使用jetcd客户端示例
public class EtcdDistributedLock {private Client client;private Lease lease;public boolean tryLock(String lockKey) throws Exception {lease = client.getLeaseClient().grant(30).get(); // 30秒租约Txn txn = client.getKVClient().txn();txn.If(new Cmp(lockKey, Cmp.Op.EQUAL, CmpTarget.version(0))).Then(Op.put(lockKey, "locked", PutOption.newBuilder().withLeaseId(lease.getID()).build())).Else(Op.get(lockKey));return txn.commit().get().isSucceeded();}
}

核心原理
基于租约(Lease)机制,利用事务操作实现原子性锁获取

优势

  1. 强一致性(Raft协议)
  2. 自动续期机制
  3. 支持公平锁/非公平锁

劣势

  1. 运维复杂度较高
  2. 客户端生态不如Redis完善
  3. 性能低于Redis

三、数据库实现方案
// 基于MySQL的乐观锁实现
public class DbDistributedLock {@Transactionalpublic boolean tryLock(String lockName) {// 使用唯一索引约束int result = jdbcTemplate.update("INSERT INTO distributed_lock(lock_name,owner) VALUES (?,?) ON DUPLICATE KEY UPDATE owner=IF(expire_time < NOW(), VALUES(owner), owner)",lockName, UUID.randomUUID().toString());return result > 0;}
}

核心原理
基于数据库唯一约束或排他锁(SELECT FOR UPDATE)

优势

  1. 无需额外中间件
  2. 实现简单快速

劣势

  1. 性能差(高并发场景容易成为瓶颈)
  2. 无自动释放机制
  3. 死锁风险较高

方案对比总结表

方案一致性性能自动释放实现复杂度适用场景
Redis最终一致支持简单高并发、允许短暂不一致
ZooKeeper强一致支持复杂强一致性要求、公平锁场景
Etcd强一致支持较复杂强一致性且需要自动续期
数据库强一致不支持简单低频访问、无中间件环境的应急方案

选型建议

  1. 追求性能 ➜ Redis(Redisson实现)
  2. 强一致性要求 ➜ ZooKeeper/Etcd
  3. 无中间件环境 ➜ 数据库方案(需谨慎处理超时)
  4. 混合使用场景 ➜ 可组合使用(如Redis做主锁,数据库做备用锁)

相关文章:

  • C++:二叉搜索树
  • MCP(Model Context Protocol,模型上下文协议)
  • 工具学习_模糊测试
  • Python+大模型 day01
  • Maven 下载安装与配置教程
  • 为什么go语言中返回的指针类型,不需要用*取值(解引用),就可以直接赋值呢?
  • Python调用SQLite及pandas相关API详解
  • Profibus DP主站转Modbus RTU/TCP网关接艾默生流量计与上位机通讯
  • ajax提交form表单数据举例
  • Ubuntu24.04编译ORB_SLAM的一系列报错解决
  • LeetCode 热题 100 105. 从前序与中序遍历序列构造二叉树
  • 季报中的FPGA行业:U型反转,春江水暖
  • Cursor 0.5版本发布,新功能介绍
  • SQL看最多的数据,但想从小到大排列看趋势
  • 家庭宽带的内网穿透实践
  • PyQt5完整指南:从入门到实践
  • 三维CAD皇冠CAD(CrownCAD)建模教程:工程图模块二
  • QT+opencv实现卡尺工具找圆、拟合圆
  • 虚幻引擎5-Unreal Engine笔记之Qt与UE中的Meta和Property
  • uniapp -- 验证码倒计时按钮组件
  • A股午后拉升,沪指收复3400点:大金融发力,两市成交超1.3万亿元
  • 阿尔巴尼亚执政党连续第四次赢得议会选举,反对党此前雇用特朗普竞选经理
  • 220名“特朗普币”持有者花1.48亿美元,获邀与特朗普共进晚餐
  • 回望乡土:对媒介化社会的反思
  • 年轻小将绽放光芒!中国短跑男女接力队直通东京世锦赛
  • 重庆荣昌区委区政府再设“答谢宴”,邀请800余名志愿者机关食堂用餐