
Go分布式锁实现方案从Redis到etcd的完整对比文章导语在分布式系统中分布式锁是保证数据一致性的核心组件。Go生态中基于Redis、etcd、ZooKeeper的分布式锁实现各有所长。本文将深入对比三种主流方案给出选型建议和生产级实现代码。一、分布式锁的核心要求一个合格的分布式锁必须满足互斥性同一时刻只有一个客户端持有锁防死锁锁超时自动释放避免客户端崩溃导致死锁解铃还须系铃人只有加锁的客户端才能释放锁容错性部分节点故障不影响锁服务二、基于Redis的分布式锁2.1 基础实现有缺陷// 简陋版——有多个严重问题funcBadLock(rdb*redis.Client,keystring,valuestring,ttl time.Duration)bool{returnrdb.SetNX(ctx,key,value,ttl).Val()}funcBadUnlock(rdb*redis.Client,keystring){rdb.Del(ctx,key)// 可能释放别人的锁}2.2 正确的Redlock实现typeRedLockstruct{clients[]*redis.Client}func(r*RedLock)Lock(ctx context.Context,keystring,ttl time.Duration)(string,error){value:uuid.New().String()deadline:time.Now().Add(ttl)quorum:len(r.clients)/21acquired:0for_,client:ranger.clients{start:time.Now()ok,err:client.SetNX(ctx,key,value,ttl).Result()iferr!nil||!ok{continue}elapsed:time.Since(start)ifelapsedttl/2{client.Del(ctx,key)continue}acquired}ifacquiredquorum{r.Unlock(ctx,key,value)return,ErrLockFailed}remaining:time.Until(deadline)ifremaining0{r.Unlock(ctx,key,value)return,ErrLockTimeout}returnvalue,nil}// 安全释放——使用Lua脚本保证原子性constunlockScript if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end func(r*RedLock)Unlock(ctx context.Context,keystring,valuestring)error{for_,client:ranger.clients{client.Eval(ctx,unlockScript,[]string{key},value)}returnnil}三、基于etcd的分布式锁funcEtcdLock(ctx context.Context,client*clientv3.Client,keystring,ttlint64)(*clientv3.LeaseGrantResponse,error){// etcd通过lease租约实现自动过期lease,err:client.Grant(ctx,ttl)iferr!nil{returnnil,err}// 使用事务实现原子性txn:client.Txn(ctx)txn.If(clientv3.Compare(clientv3.CreateRevision(key),,0)).Then(clientv3.OpPut(key,locked,clientv3.WithLease(lease.ID))).Else(clientv3.OpGet(key))resp,err:txn.Commit()iferr!nil{client.Revoke(ctx,lease.ID)returnnil,err}if!resp.Succeeded{client.Revoke(ctx,lease.ID)returnnil,ErrLockHeld}// 自动续租keepAliveCh,err:client.KeepAlive(ctx,lease.ID)iferr!nil{returnnil,err}gofunc(){forrangekeepAliveCh{// 续租成功}}()returnlease,nil}funcEtcdUnlock(ctx context.Context,client*clientv3.Client,lease*clientv3.LeaseGrantResponse)error{_,err:client.Revoke(ctx,lease.ID)returnerr}四、Redis vs etcd 对比维度Redisetcd性能极高内存操作中等Raft共识一致性最终一致性单机强一致强一致性Raft可用性主从/哨兵/集群内置Raft集群自动续期需要自行实现内置Lease KeepAliveWatch机制pub/sub不可靠Watch可靠运维成本低广泛使用中Raft集群五、选型建议// 1. 性能敏感且可容忍偶发冲突 → Redis// 2. 一致性要求极高 → etcd// 3. 已经使用Redis → Redis减少基础设施// 4. K8S环境 → etcdK8S自带// 生产推荐golang-redis自带的Redlockimportgithub.com/redis/go-redis/v9// 使用redsync实现importgoredisgithub.com/go-redsync/redsync/v4importgoredislibgithub.com/go-redsync/redsync/v4/redis/goredis/v9六、全文总结Redis分布式锁性能高但需要Redlock算法保证可靠性etcd分布式锁基于Raft强一致性适合高一致性要求场景锁必须设置超时防止死锁释放锁必须验证持有者Lua脚本或etcd的Transaction根据业务场景选择合适方案七、技术进阶展望Chubby和Paxos在分布式锁中的应用分布式锁的公平性实现K8S中ConfigMap/Endpoint实现分布式锁参考文献Redis分布式锁官方文档: https://redis.io/docs/manual/patterns/distributed-locks/Martin Kleppmann - How to do distributed lockingetcd官方文档 - Concurrency APIredsync: https://github.com/go-redsync/redsyncApache Curator实现文档