Redis 分布式锁

张开发
2026/4/21 17:03:37 15 分钟阅读

分享文章

Redis 分布式锁
如果所有服务同时争抢一个资源系统会怎样想象这样一个场景你的电商平台正在搞“秒杀”活动库存只有100件商品却有上万用户在同一毫秒点击“立即购买”。如果没有有效的协调机制多个服务实例可能同时读取到“还有库存”然后各自扣减最终导致超卖——卖出200件、300件甚至更多。这不仅造成业务损失还可能引发客户投诉和信任危机。在分布式系统中这种对共享资源的并发访问问题尤为棘手。单机锁如synchronized或ReentrantLock只在单个 JVM 内有效无法跨节点同步。这时候Redis 分布式锁就成了解决这类问题的关键工具之一。什么是 Redis 分布式锁简单来说Redis 分布式锁是一种利用 Redis 的原子操作特性在多个分布式节点之间实现互斥访问共享资源的机制。它的核心思想是谁先成功在 Redis 中“占位”谁就获得执行权其他节点必须等待锁释放后才能尝试获取。为什么选择 Redis原因有三高性能Redis 是内存数据库读写速度极快适合高并发场景。原子操作支持如SET key value NX PX命令能一次性完成“设值过期时间仅当不存在时设置”的操作。部署广泛大多数微服务架构中已集成 Redis无需额外引入新组件。如何正确实现一个 Redis 分布式锁很多人第一反应是用SETNXSet if Not eXists命令SETNX lock_key my_value如果返回 1说明加锁成功返回 0则说明锁已被占用。看起来没问题但这里有两个致命缺陷没有自动过期机制如果持有锁的服务宕机锁永远不会释放导致死锁。解锁不安全任意客户端都能执行DEL lock_key删除锁可能误删别人的锁。正确姿势带唯一标识 自动过期Redis 官方推荐使用SET命令的扩展参数SET lock_key unique_value NX PX 30000NX仅当 key 不存在时才设置相当于 SETNXPX 30000设置 30 秒自动过期防止死锁unique_value每个客户端使用唯一标识如 UUID用于后续安全解锁安全解锁必须校验 value不能直接DEL而要用 Lua 脚本保证原子性if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end这段脚本确保只有持有相同 value 的客户端才能删除锁避免误删。在 Java 中调用示例使用 JedisString lockKey order:lock; String requestId UUID.randomUUID().toString(); int expireTime 30000; // 30秒 // 尝试加锁 Boolean locked jedis.set(lockKey, requestId, NX, PX, expireTime); if (locked ! null locked) { try { // 执行业务逻辑如扣库存、创建订单 processOrder(); } finally { // 安全释放锁 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(requestId)); } }实践中的常见陷阱与解决方案1. 锁过期时间设置不当问题业务执行时间超过锁的过期时间锁提前释放其他线程进入导致并发。对策预估业务最大耗时适当延长过期时间。使用锁续期Watchdog 机制如 Redisson 的RLock会在后台定期延长锁的有效期只要业务还在运行锁就不会失效。2. 主从切换导致锁丢失问题Redis 主节点加锁后未同步到从节点主挂了从升主新主没有锁信息导致多个客户端同时获得锁。对策使用Redlock 算法由 Redis 作者提出向多个独立 Redis 节点同时加锁只有多数成功才算获取锁。但 Redlock 实现复杂且在极端网络分区下仍有争议。更推荐使用强一致性的 ZooKeeper 或 etcd来实现高可靠锁。大多数业务场景下单 Redis 实例 合理过期时间 唯一 ID 解锁已经足够。除非对一致性要求极高如金融交易否则不必过度设计。3. 忙等待 vs 阻塞等待简单实现中客户端通常采用“轮询重试”方式获取锁浪费 CPU 和网络。更优雅的方式是使用Redis 的 Pub/Sub 机制或Redisson 的 wait/notify 模型让等待者在锁释放时被唤醒。Redisson生产级分布式锁的首选对于 Java 开发者Redisson是一个成熟的 Redis 客户端内置了多种分布式锁实现RLock lock redisson.getLock(myLock); lock.lock(); // 默认30秒自动续期 try { // 业务逻辑 } finally { lock.unlock(); }它自动处理了唯一 ID 生成Lua 脚本解锁Watchdog 自动续期可重入锁支持同一线程可多次加锁大大降低了出错概率建议在生产环境中优先考虑。总结与思考Redis 分布式锁不是银弹但它是在性能与一致性之间取得良好平衡的实用方案。关键在于理解其原理避开常见陷阱并根据业务场景合理选择实现方式。值得思考的是是否真的需要分布式锁有时通过业务设计如将库存分片、使用消息队列削峰可以避免竞争比加锁更高效、更可靠。技术没有绝对的对错只有是否适合。在追求高并发的同时别忘了系统的可维护性和容错能力——毕竟一个能稳定运行的系统远比一个理论上完美的设计更有价值。

更多文章