大家好,我是方才,带过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. 1. 建一张锁表(含锁唯一标识、持有者、获取时间);

  2. 2. 拿锁:插入一条记录,成功=拿到锁,失败=锁被占;

  3. 3. 释放锁:删除对应记录;

  4. 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. 1. 建一个持久根节点(比如/lock);

  2. 2. 拿锁:每个客户端在根节点下建“临时有序节点”(比如/lock/000001);

  3. 3. 判断:如果自己的节点是当前最小的,就算拿到锁;否则监听前一个节点;

  4. 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. 1. 锁超时要合理:预估业务最大耗时(比如业务要10秒,超时设20秒),用看门狗自动续期;

  2. 2. 必须加唯一标识:用UUID或客户端ID,防止释放别人的锁;

  3. 3. 处理Redis主从丢锁:如果要强一致,用RedLock或直接选ZooKeeper/Etcd;

  4. 4. 性能优化:尽量缩小锁的范围(比如只锁库存扣减,不锁整个订单流程),能不用锁就用乐观锁;

  5. 5. 可重入实现:用ThreadLocal记录当前线程的锁重入次数,避免自己锁自己。

七、总结:记住3个核心结论

  1. 1. 简单高并发场景:优先用Redis(配Lua脚本+唯一标识),Java直接用Redisson;

  2. 2. 强一致需求场景:选ZooKeeper或Etcd(比如金融支付、数据同步);

  3. 3. 别过度设计:分布式锁没有“银弹”,重点是平衡一致性、性能和实现复杂度,贴合自己的业务。

备考资料&交流群

2025最新的系分/架构备考资料和备考交流群,扫码即可领取加入:

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

推荐阅读2025-方才的系分&架构VIP服务,助你一次性拿下软考

Logo

更多推荐