加锁必须用 SET key value EX seconds NX,value 需唯一(如 uuid),解锁和续期必须用 Lua 脚本校验 value,TTL 设为业务 P99×2,长任务需看门狗续期,Redlock 多数场景不必要。加锁必须用 SET key value EX seconds NX,别信单 SetNXGo 生态里很多教程说"用 rdb.SetNX 就行",但这是错的------老版本 Redis 不支持在 SetNX 里直接设过期时间,而 Go 客户端(比如 go-redis/v9)的 SetNX(ctx, key, val, ttl) 是靠服务端 SET ... NX EX 命令实现的。如果你连的是 Redis 2.4 或没开 Lua 支持的旧实例,它会 fallback 成两步:先 SETNX,再 EXPIRE,中间一崩溃,锁就永远卡住。value 必须是每个 goroutine 独立生成的,比如 uuid.NewString();写死成 "1" 或复用字符串,解锁时根本分不清是谁的锁EX 足够,除非你要毫秒级精度(那就用 PX);但注意 Go 的 time.Second 别误转成纳秒传给 EX,否则锁 1 秒变 10 亿秒TTL 建议设为「业务 P99 耗时 × 2」,比如导出接口最长跑 1.2s,就设 2 * time.Second;太短会导致锁提前释放,太长会让故障恢复变慢解锁必须用 Lua 脚本,GET + DEL 是经典竞态很多人写完 GET 判断值相等,再调 DEL,结果 A 拿着锁还没删,B 查到过期去抢,A 一删就把 B 刚 set 的锁干掉了------这不是 bug,是必然发生的 race condition。唯一安全解法是原子脚本:EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:key abc123Go 里用 redis.NewScript(unlockScript).Run(ctx, rdb, []string{key}, clientID),返回 int64,非 1 就代表解锁失败,得记录日志或告警别把脚本拼在代码里,用 embed.FS 或 const 管理,方便审计和灰度上线时热替换长任务必须配看门狗续期,EXPIRE 单独调等于裸奔业务执行时间不确定,锁又不能不设 TTL(否则主从切换后锁丢失),这矛盾只能靠续期解。但乱续期更危险:A 续了 B 的锁,B 还以为自己拿着锁在改数据。续期也得用 Lua 校验:if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("EXPIRE", KEYS[1], ARGV[2]) else return 0 end续期间隔建议为 TTL / 3,比如 TTL=2s,就每 600ms 续一次;太密增加 Redis 压力,太疏容易掉锁业务函数 return 前必须显式 stop 续期 goroutine,否则锁释放了,后台还在不停续------变成"幽灵续期"Redlock 不是银弹,多数场景单实例+正确实现就够用Redlock 看起来高大上,但实际落地要连 ≥3 个独立 Redis 实例、校准时钟、处理网络分区、应对幽灵锁......运维成本远超收益。除非你有金融级一致性要求,否则单实例锁只要做到 value 唯一 + Lua 解锁 + 合理 TTL + 续期,已覆盖订单幂等、库存扣减、定时任务防重跑等绝大多数场景。 幻导航网 发现优质实用网站,开启网络探索之旅!
相关推荐
2401_871696522 小时前
苹果微软双修党福音:Navicat如何优化跨系统传输性能延迟m0_493934532 小时前
生产环境SQL如何动态控制窗口的计算范围河阿里2 小时前
Redis:命令行基础语法与实战2301_816660212 小时前
golang如何实现消息批量消费_golang消息批量消费实现策略qq_189807032 小时前
SQL视图性能低怎么办_将普通视图转换为带索引的物化视图kishu_iOS&AI2 小时前
Pytorch —— 自动微分模块yejqvow122 小时前
如何在 Supabase 中安全实现用户“鼓掌”计数(防刷、防重放、防越权)m0_678485452 小时前
SQL利用窗口函数实现轻量级报表设计_实战技巧m0_747854522 小时前
CSS实现卡片式布局_浮动元素与clearfix的应用