news 2026/6/12 17:08:26

Redis - 如何使用 Redis 实现分布式锁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis - 如何使用 Redis 实现分布式锁

文章目录

  • 分布式锁的基本要求
  • 单机版:SET NX PX 一行搞定
    • 释放锁要用 Lua
  • 锁过期的另一个问题:业务超时
  • 集群版的 RedLock 算法
    • RedLock 的争议
  • Redisson:开箱即用的方案
  • 常见坑点
    • 1. 锁粒度太大
    • 2. 锁粒度太小
    • 3. 忘了 unique_value
    • 4. 没有重试机制
    • 5. 用普通 SET 代替 SETNX
  • 实践建议

在分布式系统里,多个进程需要协调访问共享资源时,分布式锁几乎是绕不开的工具。Redis 因为性能好、部署简单、命令丰富,成为实现分布式锁的最常见选择。但简单不等于容易——一把"看起来能用"的 Redis 分布式锁,真正用到生产环境,常常因为细节问题踩坑。

分布式锁的基本要求

任何一把分布式锁,都必须满足三个性质:

  1. 互斥性:同一时刻只有一个客户端能持有锁。
  2. 死锁避免:持有锁的客户端崩溃,锁要能自动释放。
  3. 解铃还须系铃人:客户端只能释放自己持有的锁,不能误删别人的。

Redis 实现分布式锁的难点,就在这三点上。

单机版:SET NX PX 一行搞定

最基础的实现:

SET lock_key unique_value NX PX30000

三个关键参数:

  • NX:key 不存在才设置,保证互斥。
  • PX 30000:30 秒后自动过期,防止死锁。
  • unique_value:客户端唯一标识,释放时校验。

加锁成功返回OK,失败返回nil

释放锁要用 Lua

释放锁不是简单的DEL。考虑这个场景:

客户端 A 加锁,PX=30s 客户端 A 业务执行 35s(GC 卡顿等原因) 锁过期,客户端 B 加锁成功 客户端 A 完成业务,DEL lock_key ← 删的是 B 的锁!

要避免这个问题,释放时必须先校验持有者。但"GET + DEL"是两条命令,中间可能被打断。所以释放必须用 Lua 脚本保证原子性:

ifredis.call('GET',KEYS[1])==ARGV[1]thenreturnredis.call('DEL',KEYS[1])elsereturn0end

锁过期的另一个问题:业务超时

PX 30s 是个两难的选择:

  • 太短:业务还没做完就过期,锁失效。
  • 太长:进程崩溃后,锁很久才能释放,影响其他客户端。

成熟的方案是引入"看门狗"(watchdog)机制:客户端启一个后台线程,定期延长锁的过期时间。Redisson 就是这么实现的——默认每 10 秒续期一次,把锁的过期时间重置回 30 秒。这样只要持有者还活着,锁就不会过期;一旦持有者挂了,看门狗也停了,锁会自然过期。

集群版的 RedLock 算法

单机 Redis 实现的锁有个根本性问题:主库挂了。如果加锁后主库还没把锁同步到从库就崩溃,哨兵切换到从库,新主库上根本没这个锁,互斥性就被打破了。

Redis 作者 Antirez 提出了 RedLock 算法,思路是:

  1. 部署 N 个独立的 Redis 实例(推荐 5 个),互不主从。
  2. 客户端依次向所有实例申请同一个锁,记录开始时间。
  3. 如果超过 N/2+1 个实例加锁成功,且总耗时小于锁的过期时间,则认为加锁成功。
  4. 如果失败,向所有实例发送释放请求(不管之前加锁是否成功)。

只要多数派实例存活且没被网络隔离,锁就能保持互斥性。

RedLock 的争议

分布式系统专家 Martin Kleppmann 写过一篇著名文章质疑 RedLock:在 GC 暂停、时钟漂移等场景下,RedLock 仍然不能保证安全性。Antirez 也回应过,但这个争论至今没有定论。

实际工程上的建议:

  • 普通业务:单机 Redis + 短 TTL + 看门狗(如 Redisson 默认实现)已经足够,简单可靠。
  • 金融级强一致:直接用 ZooKeeper 或 etcd,它们的设计就是为分布式协调而生。
  • 真要用 RedLock:考虑清楚是不是真的需要这种复杂度。

Redisson:开箱即用的方案

自己实现分布式锁很容易踩坑。Java 生态里 Redisson 是事实标准,封装好了所有细节:

RedissonClientredisson=Redisson.create(config);RLocklock=redisson.getLock("myLock");try{lock.lock();// 阻塞获取,自动续期// 业务逻辑}finally{lock.unlock();}

Redisson 提供了:

  • 自动续期(看门狗)
  • 可重入(同一线程多次获取不阻塞)
  • 公平锁(按申请顺序授予)
  • 读写锁、信号量、CountDownLatch 等更复杂的同步原语
  • 集群模式下的 RedLock 实现

Python 用redis-py自带的redis.lock,Go 用redsync,都能省掉不少基础工作。

常见坑点

1. 锁粒度太大

把整个业务都放在锁里,锁的持有时间过长,吞吐量直线下降。锁应该只保护真正有竞争的那段代码。

2. 锁粒度太小

为了优化性能把锁拆得太细,结果加多把锁反而引入死锁风险。粒度要适中。

3. 忘了 unique_value

释放锁时不校验持有者,可能误删别人的锁。这是最常见的隐藏 bug。

4. 没有重试机制

加锁失败直接报错,对业务不友好。一般要带退避重试,但要设上限避免无限等待。

5. 用普通 SET 代替 SETNX

SET key value会无条件覆盖,根本没有互斥语义。必须带 NX。

实践建议

  1. 能不用锁就不用,优先考虑用 Redis 原子操作或乐观锁解决。
  2. 生产环境用成熟库(Redisson / redis-py 内置 lock),别自己造轮子。
  3. TTL 要合理,业务正常耗时的 2-3 倍即可,配合看门狗续期。
  4. 释放锁必须用 Lua,校验持有者后再删除。
  5. 强一致场景换工具,ZooKeeper / etcd 比 Redis 更适合做分布式协调。
  6. 监控锁的等待时间,等待时间长说明竞争激烈,要么优化业务,要么调整锁粒度。

分布式锁是个看起来简单实际复杂的话题。Redis 提供的命令很基础,但要把锁用对,必须考虑过期、续期、释放、容错等一系列问题。理解这些细节,才能在真实业务里写出经得起考验的代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 17:08:16

League Akari:英雄联盟玩家的革命性本地自动化工具集

League Akari:英雄联盟玩家的革命性本地自动化工具集 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 在英雄联盟的激烈竞技中&…

作者头像 李华
网站建设 2026/6/12 17:06:58

本地消费红包系统的风控设计:动态档位、活跃系数与熔断机制

先说两个现实。实体商家这几年,不是被对手干掉的,是被流量平台架空的。抖音投了,美团上了,进店的客人越来越少,平台的佣金越来越高。自己的客户,坐在自己店里消费,最后一分都没沉淀下来。再看平…

作者头像 李华
网站建设 2026/6/12 17:06:00

A2A协议:Agent协作的轻量级语义握手协议

1. 项目概述:这不是“AI联网协议”,而是Agent协作的底层握手语言你可能已经看过不少标题里带“A2A”“Agent-to-Agent”的文章,但多数只是把几个开源项目名字堆在一起,再配上“颠覆未来”“下一代互联网”的夸张定语。我从2022年就…

作者头像 李华
网站建设 2026/6/12 16:57:06

MC9S12C32嵌入式开发实战:从经典HCS12架构到汽车电子应用

1. 项目概述:为什么MC9S12C32依然是经典之选在嵌入式开发领域,尤其是汽车电子和工业控制这两个对可靠性、实时性要求近乎苛刻的行业,选择一颗“靠谱”的微控制器(MCU)往往是项目成功的一半。从业十多年,我经…

作者头像 李华
网站建设 2026/6/12 16:57:05

MPC7400处理器架构解析:RISC、超标量与AltiVec向量技术实战

1. 项目概述:MPC7400,一个被低估的RISC性能标杆在嵌入式和高性能计算领域,PowerPC架构曾是一股不可忽视的力量。今天要聊的这颗MPC7400,就是那个时代Motorola(后来是Freescale)交出的一份经典答卷。它不是最…

作者头像 李华