架构师必知:分布式锁
在多节点的分布式系统中,控制多个客户端对共享资源的访问,确保同一时刻只有1个客户端能操作资源,避免数据错乱。• 比如库存只剩1件,2个线程同时查到“库存≥1”,都去扣减,最后库存变成-1,超卖了!• 核心:用“租约(Lease)”和“原子事务”实现,锁绑定租约,租约到期自动释放;:尽量缩小锁的范围(比如只锁库存扣减,不锁整个订单流程),能不用锁就用乐观锁;:分布式锁没有“银弹”,重点是平衡一致性、
大家好,我是方才,带过15人研发团队,有4年团队管理和架构设计经验。
先放个福利:文末有2025年最新系统架构师备考资料,还附备考交流群,看完文章记得去领,备考路上一起组队效率更高~
一、先搞懂:分布式锁到底是啥?(大白话版)
你给女朋友打电话,听到“您拨打的电话正在通话中”——这就是最直观的“分布式锁”逻辑(你级别太低啦,没抢到锁!):
-
• 「客户端」:所有想给你女朋友打电话的人;
-
• 「共享资源」:你女朋友、的手机通话权限;
-
• 「锁的作用」:同一时间,只有1个人能打通电话。
放到技术场景里,分布式锁的核心是:在多节点的分布式系统中,控制多个客户端对共享资源的访问,确保同一时刻只有1个客户端能操作资源,避免数据错乱。
二、为什么必须用分布式锁?3个核心需求+3个高频场景
1. 分布式锁的4个核心要求(前3个必满足)
-
• 互斥性:同一时间,只有1个客户端能拿到锁;
-
• 防死锁:锁必须有超时自动释放机制(比如客户端崩溃了,锁不能一直占着);
-
• 高可用:锁服务不能单点故障,多数节点活着就能用;
-
• 可重入/公平性(可选):同一客户端能多次拿同一把锁,或按请求顺序给锁(避免“饿死”)。
2. 这些场景必须用,否则会出大问题
-
• 本质就是一个:资源竞争控制
-
• 库存防超卖:秒杀时100件商品,并发下若没锁,可能被多卖出去几十件;
-
• 分布式任务调度:比如定时任务,不能让多个节点同时执行(否则重复发消息、重复计算);
-
• 保证幂等性:避免重复处理请求(比如用户反复点支付,不能扣多次钱)。
三、秒杀超卖?用分布式锁怎么解决?(附对比图)
秒杀是最典型的“分布式锁应用场景”,我们直接看问题和方案:
1. 不加锁的坑:库存直接超卖
秒杀扣库存的逻辑很简单:先查库存,够了就扣减。但并发下会出问题:
-
• 比如库存只剩1件,2个线程同时查到“库存≥1”,都去扣减,最后库存变成-1,超卖了!
(不加锁的问题流程)
2. 加锁后的正确姿势
在“查库存+扣库存”前加分布式锁,确保只有1个线程能进入流程:
-
• 实际秒杀中,库存会提前存到Redis(提升性能),这时候用Redis分布式锁最方便。
(加锁后防超卖)
四、4种常见实现方式:优缺点+步骤拆解
1. 基于数据库:最简单但性能差
-
• 怎么实现:用MySQL唯一索引,插入记录=拿锁,删除记录=释放锁;
-
• 优点:不用额外加组件,代码简单;
-
• 缺点:高并发下数据库扛不住,还得写定时任务清理超时锁(可靠性低)。
具体步骤:
-
1. 建一张锁表(含锁唯一标识、持有者、获取时间);
-
2. 拿锁:插入一条记录,成功=拿到锁,失败=锁被占;
-
3. 释放锁:删除对应记录;
-
4. 防死锁:给锁设超时时间,超时后强制释放。
2. 基于Redis:高并发首选(附代码)
Redis是最常用的方案,核心靠“原子操作”保证可靠性:
1)简单实现(用set nx ex命令)
# 原子操作:键不存在才设置(NX),同时设30秒超时(EX)
SET lock_key 你的UUID NX EX 30
-
• 拿锁:执行上面的命令,成功=拿到锁;
-
• 释放锁:用Lua脚本先验证UUID(防止误删别人的锁),再删除;
-
• 坑点:Redis主从切换时可能丢锁(因为Redis是AP模型,弱一致性)。
2)RedLock算法(解决主从丢锁问题)
-
• 逻辑:向多个独立Redis实例申请锁,超过半数成功且总耗时<锁超时,才算拿锁成功;
-
• 争议:对时钟同步要求高,性能开销大,极端网络分区下仍有风险。
3)Java推荐用Redisson
封装好了锁的获取、释放、自动续期(看门狗),一行代码搞定,不用自己写复杂逻辑。
3. 基于ZooKeeper:强一致首选
ZooKeeper是CP模型(优先保证一致性),适合对“强一致”要求高的场景:
实现逻辑:
-
1. 建一个持久根节点(比如/lock);
-
2. 拿锁:每个客户端在根节点下建“临时有序节点”(比如/lock/000001);
-
3. 判断:如果自己的节点是当前最小的,就算拿到锁;否则监听前一个节点;
-
4. 释放锁:客户端断开连接,临时节点自动删除,下一个节点会收到通知。
优点:临时节点自动释放(防死锁),天然公平锁;
缺点:性能比Redis低,频繁创建节点+监听可能引发“羊群效应”(大量节点监听同一个节点)。
4. 基于Etcd:新一代强一致方案
Etcd和ZooKeeper类似,但性能更好、部署更简单:
-
• 核心:用“租约(Lease)”和“原子事务”实现,锁绑定租约,租约到期自动释放;
-
• 工具:可以用etcdctl命令行,或Go的concurrency包(Java也有客户端)。
和ZooKeeper的区别:
|
对比项 |
ZooKeeper |
Etcd |
|---|---|---|
|
性能 |
中 |
高(极限10K QPS) |
|
部署复杂度 |
高 |
低(Go语言实现) |
|
数据模型 |
树形结构 |
扁平化Key-Value |
|
协议 |
ZAB |
Raft |
五、怎么选?一张表搞定选型
|
实现方式 |
一致性 |
性能 |
复杂度 |
适用场景 |
|---|---|---|---|---|
|
数据库 |
弱 |
低 |
低 |
简单场景、低并发 |
|
Redis |
中 |
高 |
中 |
高并发、能接受偶尔冲突 |
|
RedLock |
强 |
中 |
高 |
强一致需求、能接受性能损耗 |
|
ZooKeeper |
强 |
中 |
高 |
公平锁、强一致场景 |
|
Etcd |
强 |
高 |
中 |
高并发+强一致需求 |
六、避坑指南:5个必须注意的点
-
1. 锁超时要合理:预估业务最大耗时(比如业务要10秒,超时设20秒),用看门狗自动续期;
-
2. 必须加唯一标识:用UUID或客户端ID,防止释放别人的锁;
-
3. 处理Redis主从丢锁:如果要强一致,用RedLock或直接选ZooKeeper/Etcd;
-
4. 性能优化:尽量缩小锁的范围(比如只锁库存扣减,不锁整个订单流程),能不用锁就用乐观锁;
-
5. 可重入实现:用ThreadLocal记录当前线程的锁重入次数,避免自己锁自己。
七、总结:记住3个核心结论
-
1. 简单高并发场景:优先用Redis(配Lua脚本+唯一标识),Java直接用Redisson;
-
2. 强一致需求场景:选ZooKeeper或Etcd(比如金融支付、数据同步);
-
3. 别过度设计:分布式锁没有“银弹”,重点是平衡一致性、性能和实现复杂度,贴合自己的业务。
备考资料&交流群
2025最新的系分/架构备考资料和备考交流群,扫码即可领取加入:

最后,方才软考架构师刷题系统已经上线了,欢迎大家使用(公众号后台回复【666】,即可一键体验,pc端地址:https://fangcaicoding.cn/papers)。
更多推荐

:
所有评论(0)