恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Golang Redis 分布式锁

  • 首页
  • 资讯中心
  • /
  • Golang Redis 分布式锁

相关资讯

Hetzner 云服务器 云原生实战:部署架构与网络验收 2026/8/26 17:42:21
Linode 实例 云原生实战:部署架构与网络验收 2026/8/26 17:42:21
OVH 欧洲云 云原生实战:部署架构与网络验收 2026/8/26 17:42:21

最新资讯

【高级】系统架构师 | 案例分析专栏-信息安全案例详解
《C++深度解构03》类和对象(中)——从零拆解六大默认成员函数底层逻辑
TCP自定义协议——学习到的编译工具
Deep Learning for Computer Vision - Part1
企业选择WAF的十大关键指标——从性能到成本全对比
水域救援割绳刀怎么选?看懂性能参数,选对靠谱生产厂家

今日推荐

Python random 模块常用函数详解:从入门到实战
Hermes接入团队协作后,我推翻了三个效率假设
免费AI大模型调教指南:打造专属网文写作助手

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

Golang Redis 分布式锁

发布时间:2026/8/26 17:42:21
Golang Redis 分布式锁 Redis 分布式锁一、为什么需要分布式锁单机应用里sync.Mutex就能保护临界区。但在微服务集群中多个进程——甚至跨机器——同时操作共享资源减库存、抢优惠券、定时任务去重单机锁就无能为力了。这时候需要一个所有节点都能访问的协调器来做锁仲裁Redis 就是最常见的实现载体。核心要求只有三条要求说明互斥性同一时刻只有一个人能拿到锁不死锁即使持有者崩溃锁最终必须释放靠 TTL解铃还须系铃人释放锁时校验身份不能删除别人拿到的锁二、一步一步构造可靠的分布式锁第一版只用 SETNX❌ 有缺陷// ❌ 危险写法SETNX 和 EXPIRE 是两条命令中间可能崩溃导致死锁ok,_:rdb.SetNX(ctx,lock:order,1,0).Result()ifok{rdb.Expire(ctx,lock:order,30*time.Second)// 万一这里挂了锁永远不会释放deferrdb.Del(ctx,lock:order)// 还可能误删别人的锁// 业务逻辑...}问题一SETNX和EXPIRE不是原子的中间崩溃就死锁了。问题二直接DEL不验证身份如果锁过期被其他人抢占DEL会把别人的锁删掉。第二版SET NX EX 原子命令✅ 解决了原子性Redis 2.6.12 之后SET命令可以一次带上NX和EX/PX// ✅ 单条命令原子地完成不存在就创建 设置过期ok,_:rdb.SetNX(ctx,lock:order,token,30*time.Second).Result()但释放时还是不能直接DEL——需要验证这把锁是不是自己拿到的。第三版唯一令牌 Lua 原子释放✅ 生产可用关键思路加锁时给锁存一个唯一 token比如 UUID释放时先比较令牌是否匹配匹配才删除。比较 删除这步必须用 Lua 脚本因为在 Redis 中 Lua 脚本是原子执行的。// 加锁给这把锁贴上一个独一无二的标签funcAcquireLock(ctx context.Context,rdb*redis.Client,keystring,ttl time.Duration)(string,error){token:randomToken()// 每次加锁生成一个新 tokenok,err:rdb.SetNX(ctx,key,token,ttl).Result()iferr!nil{return,fmt.Errorf(acquire lock: %w,err)}if!ok{return,ErrLockHeld}returntoken,nil}// 释放锁Lua 脚本保证检查 删除是原子的varreleaseScriptredis.NewScript( if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end )funcReleaseLock(ctx context.Context,rdb*redis.Client,key,tokenstring)error{n,err:releaseScript.Run(ctx,rdb,[]string{key},token).Int()iferr!nil{returnfmt.Errorf(release lock: %w,err)}ifn0{returnErrLockExpired// token 不匹配说明锁已过期或被其他人抢占}returnnil}为什么必须用 Lua如果用 Go 代码先GET再DEL这两步之间有网络延迟锁可能恰好过期、被其他人抢走就会误删。Lua 在 Redis 单线程里一气呵成。第四版带重试的加锁拿不到锁时别立刻放弃加一个自旋重试funcAcquireLockWithRetry(ctx context.Context,rdb*redis.Client,keystring,ttl,retryInterval time.Duration,maxRetriesint)(string,error){fori:0;imaxRetries;i{token,err:AcquireLock(ctx,rdb,key,ttl)iferrErrLockHeld{ifimaxRetries{return,ErrLockHeld}select{case-time.After(retryInterval):continuecase-ctx.Done():return,ctx.Err()}}returntoken,err}return,ErrLockHeld}简而言之等一等、再试试——但不等太久用 ctx 控制超时。三、进阶锁过期比任务快怎么办假设 TTL30秒但业务跑了 35 秒。锁早就过期被释放第二个节点拿到锁进场——两个节点同时跑分布式锁名存实亡。方案A看门狗Watchdog自动续期加锁后启动一个后台 goroutine每隔TTL / 3时间用EXPIRE续命。任务完成时关闭看门狗。核心机制不要让锁在你还在干活的时候就过期了。funcAcquireLockWithWatchDog(ctx context.Context,rdb*redis.Client,keystring,ttl time.Duration)(string,context.CancelFunc,error){token,err:AcquireLock(ctx,rdb,key,ttl)iferr!nil{return,nil,err}// 启动看门狗renewCtx,cancel:context.WithCancel(context.Background())gofunc(){ticker:time.NewTicker(ttl/3)deferticker.Stop()for{select{case-ticker.C:// 续期需验证 token 是否还匹配renewScript.Run(ctx,rdb,[]string{key},token,int64(ttl.Seconds()))case-renewCtx.Done():return}}}()returntoken,cancel,nil}虽然看门狗把窗口缩小了很多但它不能从根本上消除问题——如果进程整个 GC 暂停了呢这时候需要更硬核的手段。方案BFencing Token栅栏令牌原理每次加锁成功时返回一个单调递增的序号。写共享资源时带上这个序号资源层拒绝小于当前已处理序号的请求。即使旧持有者睡醒它的请求也被截胡了。可以用 Redis 的INCR原子增长来生成序号。实用建议日常业务用看门狗就够了。只有在对数据一致性极其敏感的金融场景才上栅栏令牌。四、Redlock多节点 Redis 锁单节点 Redis 有 SPOF单点故障风险——如果主节点宕机且异步复制还没同步到从节点锁数据就丢了。Redlock 算法Redis 作者 Antirez 提出的思路同时在 N 个独立 Redis 节点上获取锁N 为奇数通常 N≥5只有拿到过半数的成功才算加锁成功。// Redlock 的核心逻辑在多数节点上都拿到锁才算成功func(r*RedLock)Lock(ctx context.Context,keystring,ttl time.Duration)(string,error){token:randomToken()deadline:time.Now().Add(ttl/2)// 加锁总耗时不能超过 TTL 的一半success:0for_,client:ranger.clients{ok,_:client.SetNX(ctx,key,token,ttl).Result()ifok{success}iftime.Now().After(deadline){break}}// 过半成功 → 加锁成功否则回滚所有节点ifsuccesslen(r.clients)/21{r.Unlock(ctx,key,token)return,ErrLockHeld}returntoken,nil}实际上开源的go-redsync/redsync库已经封装好了 Redlock直接拿来用即可。五、本章要点回顾要点一句话总结SETNX 不等于分布式锁SETNX 只是不存在才设值还需要 TTL、token、原子释放Lua 释放是刚需比较 删除必须原子化否则有竞态看门狗是标配业务跑得比 TTL 长时需要后台续期栅栏令牌是最强保障即使锁失效数据层也能拒绝过期请求Redlock 对抗节点故障多节点过半确认牺牲一点性能换一致性一句箴言分布式锁的核心不是锁而是谁的锁。token 校验是整个设计的灵魂。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号