发布于2026-07-09 阅读(0)
扫一扫,手机访问
先说几个核心判断:如果你还指望sync.Mutex能在多实例环境下当分布式锁使——趁早打消这个念头,它只认单进程,部署一扩容就彻底失灵。真正经得起线上考验的方案,掰着指头数也就两个:Redis(靠原子SET + Lua脚本做校验)和etcd(靠lease + Txn的CAS机制)。选型没有银弹,关键看你愿意在一致性、延迟和运维复杂度上各自让步到什么程度。

不少同学写rdb.SetNX(ctx, key, val, ttl),看着挺简洁,但底层实现可能拆成两段——尤其老版本客户端。中间一旦进程崩溃,锁就永久挂在那了。正确的姿势就一条:强制走原子SET。
SET lock:order:123 "a1b2c3" EX 30 NX:EX是秒级,PX是毫秒级,必须显式写清楚,否则没有自动释放机制"OK"才算抢到锁,返回nil就是失败,别光靠error判断uuid.NewString(),后续所有操作全靠它校验所有权go-redis/v9的话,直接调client.Set(ctx, key, value, ttl).Result()就OK,默认走原子SET直接DEL key或者GET+DEL组合?那是给误删留后门——A加的锁很可能被B顺手删掉。所有关键动作必须收进Lua脚本,在Redis单线程里原子执行:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 endif redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("EXPIRE", KEYS[1], ARGV[2]) else return 0 endKEYS,value和TTL放ARGV,类型要保持一致——比如TTL传整数,Lua里千万别当字符串比较int64(0)代表校验失败,这时业务该立即中止,而不是傻傻重试——锁已经不属于你了Redis主从异步复制有脑裂风险(两个节点可能同时认为自己持有锁),etcd靠Raft日志同步能从根子上杜绝这点,代价是延迟稍高、API更重:
client.Grant(ctx, 15)拿到lease ID,再client.Put(ctx, key, clientID, clientv3.WithLease(leaseID))Txn()做CAS校验:OpGet读当前值 + Compare判断是否等于自己clientID + OpPut写入,三个操作打包提交client.KeepAliveOnce(ctx, leaseID),一旦失败就得主动Revoke并退出/locks/order/12345,避免和业务key冲突;context超时别设太短,网络抖动时容易提前cancel导致lease过期最容易翻车的其实是锁的「生命周期管理」:Redis方案里,续期goroutine如果没跟着主业务的cancel一起退出,会持续发无效请求;etcd方案里,忘了KeepAlive或者没处理Revoke异常,锁就永远卡在那。这些问题在线上压测时往往不暴露,一到流量高峰或网络波动才突然蹦出来——到时候再排查,代价可就大了。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8