记一次Redis分布式锁实现线上排查:Redlock算法与 Lua 脚本

<p style="text-align:center"><img src="https://platform-outputs.agnes-ai.space/images/t2i/task_oItDyjsqtI4IMZRbH62BvAYloCfqmw77/output_9a9cc4490fe3466dae3ccc91caa76962.png" alt="封面图" style="max-width:100%;height:auto" /></p>

记一次Redis分布式锁实现线上排查:Redlock算法与 Lua 脚本

昨天半夜监控报警,订单系统重复扣款。查了半天,又是分布式锁没扛住并发。干了十年运维,这种坑见过太多,今天把 Redis 分布式锁实现里最要命的 3 个点拆清楚,别让你家系统也背锅。

坑一:SETNX 与 EXPIRE 非原子操作

很多新人喜欢把加锁和设过期时间分开写,这是找死。网络抖动导致设过期时间失败,锁就永不过期,死锁等着您。

复制代码
# 错误示范
redis.setnx(lock_key, request_id)
redis.expire(lock_key, 30)

**效果说明:** 若第二句执行前服务崩溃,锁永久存在,后续请求全部阻塞,系统瘫痪。

坑二:单节点故障导致锁失效

单主 Redis 挂了,主从切换瞬间,锁可能还没同步到从库,新主认为锁不存在,并发瞬间击穿。这时候必须上 Lua 脚本保证原子性。

复制代码
-- 正确示范:加锁与过期原子执行
if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then
    return redis.call('expire', KEYS[1], ARGV[2])
else
    return 0
end

**效果说明:** 脚本在 Redis 服务端原子执行,要么全成功,要么全失败,杜绝中间状态导致的锁泄露。

坑三:脑裂场景下的 Redlock 算法

高可用集群下,单锁不可靠。Redlock 算法要求客户端向 5 个独立节点申请锁,至少 3 个成功才算拿到锁,防止脑裂。

复制代码
# Redlock 逻辑示意
success_count = 0
for node in redis_nodes:
    if node.set(lock_key, val, nx=True, ex=30):
        success_count += 1
if success_count >= 3:
    return True # 锁获取成功

**效果说明:** 即使 2 个节点宕机或网络分区,只要多数派共识达成,锁依然安全,避免双主同时写入。

老兵避坑清单

  1. **锁超时时间**:必须大于业务执行时间,防止业务没完锁已释放。

  2. **解锁校验**:释放锁时校验 request_id,防止误删别人的锁。

  3. **时钟漂移**:Redlock 需扣除网络耗时,避免客户端时间不准导致锁过早失效。

  4. **重试机制**:获取锁失败需带退避策略重试,避免雪崩。

  5. **看门狗**:业务耗时不确定时,用看门狗自动续期,别硬设超时。

分布式锁不是银弹,理解底层原理才能稳住生产。你在线上遇到过锁失效导致的脏数据吗?是怎么解决的?

相关推荐
温暖小土3 小时前
在 CentOS 上安装 Redis Stack
linux·redis·centos
Mikko75 小时前
Redis 连接数满了服务端有错误信息吗?连接数不增加却命令超时?6.2 实测连接池、超时与大 key 怎么判定
java·spring boot·redis·后端
无言听风雨丶6 小时前
Redis 运维实战与排错
运维·数据库·redis
字节探索11 小时前
Redis 分布式锁:从 SETNX 误删到看门狗自动续期的原理与实践
redis·分布式
真上帝的左手11 小时前
30. 技术专题-‌应用框架组件-缓存-Redis
数据库·redis·分布式·缓存·中间件
bksczm12 小时前
C++Redis开发(Ubuntu 22.04)
数据库·redis·缓存
goehou12 小时前
缓存该删还是该改,LLM 服务里的双写问题
redis·缓存·llm·一致性·系统设计
余槐i13 小时前
Spring Boot 3.2 偶发500无日志:线程池复用导致请求上下文丢失
spring boot·redis·后端·mysql·性能优化
IT规划师14 小时前
Redis 数据类型全解 - ZSet:两个编码,七种玩法(五)
redis·.net
我不会起名字3221 天前
Redis 从 0 到 1(十七):订单 30 分钟不支付自动关闭——ZSet 延迟队列 Go 实现
redis·go·延迟队列·zset·订单