
1. 从单机锁到分布式锁的本质挑战在单机环境下我们使用synchronized或ReentrantLock就能解决线程安全问题因为这些锁的实现依赖于JVM内存中的状态管理。但当我们把系统拆分成分布式架构时事情就变得复杂了——不同服务实例运行在不同的JVM进程中甚至部署在不同的物理机器上传统的线程锁机制完全失效。这就是分布式锁要解决的核心问题在分布式系统中如何实现跨JVM、跨机器的互斥访问控制Redis作为高性能的内存数据库自然成为了实现分布式锁的热门选择。但实现一个生产可用的分布式锁远比想象中复杂。2. Setnx最基础的Redis锁实现2.1 基本实现原理Redis的SETNXSET if Not eXists命令是实现分布式锁最原始的方式SETNX lock_key unique_value当key不存在时设置成功返回1否则返回0。获取锁的客户端执行完操作后通过DEL命令释放锁。2.2 致命缺陷与改进方案这种简单实现有几个严重问题死锁风险如果客户端获取锁后崩溃锁将永远无法释放非阻塞获取获取失败后没有重试机制非可重入同一客户端无法重复获取锁改进后的典型实现会加入过期时间SET lock_key unique_value NX PX 30000这个原子命令解决了死锁问题但仍然存在其他隐患。关键经验生产环境中永远不要单独使用SETNX必须配合过期时间使用3. 解决锁过期与业务执行时间的矛盾3.1 锁续约机制设置过期时间带来了新问题如果业务执行时间超过锁过期时间怎么办这时其他客户端可能获取到锁导致互斥失效。解决方案是引入看门狗机制——启动一个后台线程定期检查锁状态如果业务仍在执行就延长锁过期时间。Redisson框架中的实现逻辑如下// Redisson看门狗核心逻辑 private void scheduleExpirationRenewal() { Thread task new Thread(() - { while (true) { // 每1/3过期时间续约一次 Thread.sleep(lockWatchdogTimeout / 3); // 执行Lua脚本续约 renewExpiration(); } }); task.start(); }3.2 锁的唯一标识问题另一个常见错误是不同客户端互相释放对方的锁。解决方案是为每个锁设置唯一值String clientId UUID.randomUUID().toString(); redisTemplate.opsForValue().setIfAbsent(lock, clientId, 30, TimeUnit.SECONDS); // 释放时验证 if (clientId.equals(redisTemplate.opsForValue().get(lock))) { redisTemplate.delete(lock); }4. RedlockRedis官方分布式锁算法4.1 算法核心步骤Redis作者提出的Redlock算法通过多个独立Redis节点提高可靠性获取当前时间T1依次向N个Redis实例获取锁使用相同的key和随机值计算获取锁总耗时T2-T1只有当大多数节点N/21获取成功且总耗时小于锁有效期才认为获取成功锁的实际有效时间 初始有效时间 - 获取锁总耗时如果获取失败向所有节点发起释放锁请求4.2 生产环境实现要点节点选择至少5个独立Redis实例物理隔离的机器时钟同步所有节点必须使用NTP保持时钟同步重试策略设置合理的重试间隔和次数性能考量网络延迟会影响锁获取成功率// Redisson RedLock使用示例 RLock lock1 redissonInstance1.getLock(lock); RLock lock2 redissonInstance2.getLock(lock); RLock lock3 redissonInstance3.getLock(lock); RedissonRedLock lock new RedissonRedLock(lock1, lock2, lock3); lock.lock(); try { // 业务逻辑 } finally { lock.unlock(); }5. 生产级分布式锁的16个关键考量5.1 性能与可靠性平衡锁粒度控制根据业务场景选择合适粒度的锁超时时间设置建议设置自动续约避免业务未完成锁已过期降级策略当Redis不可用时如何保证系统继续运行5.2 异常处理方案网络分区处理CAP理论下的取舍客户端长时间GC导致锁失效Redis节点故障转移时的锁状态一致性5.3 监控与运维锁等待时间监控死锁检测机制锁使用统计报表6. 分布式锁的替代方案对比6.1 ZooKeeper实现对比特性RedisZooKeeper性能高(10w QPS)中(万级QPS)一致性模型最终一致强一致实现复杂度中等较高适用场景高性能要求强一致性要求6.2 数据库乐观锁方案对于低频竞争场景可以考虑使用数据库版本号实现乐观锁UPDATE inventory SET stockstock-1, versionversion1 WHERE product_id100 AND versionold_version;7. 典型业务场景实战7.1 秒杀系统实现public boolean seckill(Long productId, Long userId) { String lockKey seckill: productId; String clientId UUID.randomUUID().toString(); try { // 获取分布式锁 boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS); if (!locked) { return false; } // 检查库存 Integer stock (Integer)redisTemplate.opsForValue().get(stock: productId); if (stock 0) { return false; } // 扣减库存 redisTemplate.opsForValue().decrement(stock: productId); // 创建订单 createOrder(productId, userId); return true; } finally { // 释放锁 if (clientId.equals(redisTemplate.opsForValue().get(lockKey))) { redisTemplate.delete(lockKey); } } }7.2 分布式定时任务调度使用Redis锁确保集群中只有一个节点执行定时任务Scheduled(cron 0 0/5 * * * ?) public void scheduledTask() { String lockKey scheduled:report; try { if (redisLock.tryLock(lockKey, 10, 60)) { // 执行业务逻辑 generateDailyReport(); } } finally { redisLock.unlock(lockKey); } }8. 高级话题与最佳实践8.1 锁分段提升并发性能对于高并发场景可以采用锁分段技术// 将商品ID哈希到16个分片 int segment productId.hashCode() 15; String lockKey product_lock: segment; // 获取分段锁 RLock segmentLock redisson.getLock(lockKey); segmentLock.lock(); try { // 处理业务 } finally { segmentLock.unlock(); }8.2 读写锁实现Redisson提供的分布式读写锁RReadWriteLock rwLock redisson.getReadWriteLock(anyRWLock); // 读锁 rwLock.readLock().lock(); try { // 多个线程可以同时获取读锁 } finally { rwLock.readLock().unlock(); } // 写锁 rwLock.writeLock().lock(); try { // 只有一个线程可以获取写锁 } finally { rwLock.writeLock().unlock(); }8.3 红锁算法的争议与改进Martin Kleppmann曾指出Redlock可能存在的问题引发了业界广泛讨论。在实际应用中我们可以采取以下改进措施增加fencing token机制结合业务特点设计补偿机制实现锁的可观测性监控、日志9. 性能优化实战技巧连接池配置合理设置最大连接数和超时时间# Redisson配置 spring.redis.redisson.configclasspath:/redisson.yamlLua脚本优化将多个操作合并为原子性脚本-- 锁续约脚本 if redis.call(get,KEYS[1]) ARGV[1] then return redis.call(pexpire,KEYS[1],ARGV[2]) else return 0 end网络优化使用Redis集群时注意节点分布减少网络延迟10. 故障排查手册10.1 常见问题速查表问题现象可能原因解决方案锁获取时间过长Redis负载过高/网络延迟扩容Redis/优化网络/调整超时时间锁被意外释放业务执行超锁过期时间实现锁续约机制/延长过期时间不同客户端互相释放锁未使用唯一标识确保每个锁有唯一值并验证Redis故障后锁状态不一致主从切换导致数据丢失使用Redlock/增加Redis持久化配置10.2 诊断工具推荐Redis慢查询日志分析Redisson调试日志配置config.setLockWatchdogTimeout(30000); config.setEventLoopGroup(new NioEventLoopGroup());Redis监控指标命令执行时间内存使用情况网络流量11. 从Redis锁到分布式协调分布式锁只是分布式协调的一个特例。在更复杂的场景下我们可能需要分布式队列Redis Stream分布式信号量Redisson Semaphore分布式计数器Redis INCR发布订阅模式Redis Pub/Sub每种协调机制都有其适用场景理解它们的特性和限制才能设计出可靠的分布式系统。