单机时代,一个 synchronized 就能挡住并发。系统拆成微服务之后,麻烦来了:两个实例同时抢到同一张订单、定时任务在三个节点上各跑了一遍、库存被两个请求同时扣减——进程内锁管不到别的机器,必须有把"全集群都认"的锁。
核心需求就一句话:在分布式环境里,让同一时刻只有一个节点能执行某段逻辑。 业界有三条主流路线:数据库锁、Redis 锁、Zookeeper/etcd 锁,可靠性、性能、复杂度各不相同。
原理: 用数据库的行锁或唯一约束实现。典型做法两种:select ... for update 悲观锁,或者建一张锁表、靠唯一索引插入成功即获锁。释放就是提交事务或删记录。
优点:
缺点:
适用场景: 并发量不大、锁竞争不激烈的系统,或者锁逻辑和业务事务强绑定的场景(比如扣款时锁账户行)。小团队起步阶段的务实选择。
原理: 用 SET key value NX PX 原子命令加锁,value 存唯一标识,配合 Lua 脚本安全解锁。要更强可靠性就上 RedLock(多节点加锁)或直接用 Redisson 这类封装好的客户端,自带看门狗续期。
优点:
缺点:
适用场景: 绝大多数互联网业务场景:防重复提交、任务防重、库存扣减。性能优先、对极端小概率的锁失效能容忍(有幂等兜底)的场景,这是默认答案。
原理: 利用 ZK 的临时顺序节点:所有客户端在锁节点下创建临时顺序节点,序号最小的获得锁,其余监听前一个节点。客户端宕机,临时节点自动删除,锁自动释放。etcd 的 lease + revision 机制同理。
优点:
缺点:
适用场景: 金融级强一致场景:资金结算、核心交易的并发控制。或者系统本来就有 ZK/etcd 基础设施,顺手用。单纯为锁引入 ZK 集群,多数团队不值当。
场景特征 | 推荐方案 |
|---|---|
并发不高、锁与业务事务强绑定 | 数据库锁 |
高并发、性能优先、有幂等兜底 | Redis 锁(Redisson) |
金融级强一致、已有 ZK/etcd 设施 | Zookeeper / etcd 锁 |
一个务实的共识:锁负责挡大概率冲突,幂等负责兜小概率漏网。 不管选哪种锁,关键操作都该有幂等设计(唯一约束、去重表、状态机校验)兜底。把正确性完全押在锁上的系统,早晚出事。
第一件:明确锁的粒度和时长预期。 锁订单号还是锁用户?业务最长执行多久?这两个问题决定 key 设计和过期时间,也是后续所有坑的源头。粒度能小不大,时长按 P99 执行时间加缓冲。
第二件:锁失败要有明确策略。 抢不到锁是直接失败、排队等待还是稍后重试?不同业务答案不同:用户请求适合快速失败,后台任务适合重试。没定策略就上线,用户会看到莫名其妙的报错。
第三件:监控锁的健康度。 锁等待时长、获取失败率、锁持有时间分布,这些指标要进监控。锁竞争悄悄恶化是系统变慢的隐形推手,等用户投诉就晚了。
分布式锁没有银弹:Redis 快但有极端场景的豁口,ZK 稳但重,数据库简单但慢。先想清楚业务能容忍什么——能容忍小概率冲突就选性能,不能容忍就选可靠性,但无论如何,幂等兜底都别省。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。