资讯动态

高并发分布式锁选型实战:Redis、ZooKeeper对比与踩坑总结

发布时间:2026/9/16 1:39:44 来源:尧图企业网站定制
我们在做高并发系统时迟早会碰到“分布式锁”这道坎。单机时代一个synchronized就够了但应用拆成多个实例部署之后同一个请求可能落到不同机器上JVM 自带的锁根本管不住跨进程的互斥。这个标题下面藏着的其实是三件事锁要解决什么问题、有哪些主流方案、以及线上真正跑起来后哪些细节会坑到你。这篇文章我把自己这些年做高并发分布式锁的选型思路、实现方案对比、以及踩过的线上问题都整理出来。无论你是刚开始接触分布式锁还是已经在某个方案上吃过亏想换个思路这篇内容都能给你一个比较完整的决策框架。我会直接用从业者的口吻讲不绕弯子把关键细节摊开说。1. 高并发分布式锁的本质到底要解决什么问题1.1 单机锁失效的那一刻先看一个最常见的问题场景你有一个定时任务每天凌晨要去扫表、跑批、刷新缓存。单机部署的时候一个synchronized或者ReentrantLock就能保证同一时刻只有一个线程在执行。但系统一上微服务实例从 1 台变成 3 台同样的定时任务在三台机器上同时触发这时候问题就来了——三个进程同时在跑同一个批处理任务数据被重复处理缓存被重复刷新严重一点还会把不正确的数据写进数据库。这就是分布式锁存在的根本原因在分布式环境下让多个进程之间对同一个共享资源的访问做到互斥。它的本质和单机锁一样只是作用域从“进程内”扩大到了“跨进程”。还有一类典型场景是秒杀、抢购、库存扣减。用户请求经过负载均衡分发到不同实例如果不用分布式锁把库存操作串行化就会出现超卖。这里有个容易混淆的点很多人会把分布式锁和数据库事务、乐观锁混在一起用但其实它们解决的是不同层面的问题。事务保证的是数据一致性乐观锁保证的是更新不丢失而分布式锁保证的是“同一时刻只能有一个执行者”这个互斥语义。1.2 一把合格的分布式锁需要满足哪些条件在对比方案之前先建立一个评价基准。我在选型时基本会从六个维度去衡量一个分布式锁方案互斥性任意时刻只能有一个客户端持有锁。这条是分布式锁的底线做不到就不叫锁。安全性锁不能被误删。比如 A 持有锁结果 B 把 A 的锁删了那互斥性就瞬间被破坏。这个问题在 Redis 方案里特别常见后面我会详细讲。死锁防护客户端崩溃或者网络分区后锁必须能被自动释放不能死等。也就是说锁必须有“过期时间”或者“会话绑定”机制。容错性锁服务本身不能是单点。如果实现锁的组件挂了不能导致所有业务全挂。性能获取锁和释放锁的开销要足够低不能成为系统吞吐量的瓶颈。可重入性同一个线程能否重复获取同一把锁。这个要看具体业务需求不是所有场景都需要。这六个维度在不同的技术方案上有各自的取舍。不存在“什么都好”的方案只有“最适合当前场景”的方案。后面的方案对比其实就是围绕这几个维度展开的。2. 四种主流实现方案数据库、Redis、ZooKeeper、etcd2.1 数据库方案老派但依然有生存空间数据库分布式锁是最容易想到的实现方式因为它不需要额外引入中间件直接用现有数据库的能力就能做。实现上分两种悲观锁和乐观锁。悲观锁的做法是利用数据库的行级锁典型语句是SELECT * FROM t_lock WHERE resource_id order_123 FOR UPDATE;这条语句执行后其他事务再要对同一行做FOR UPDATE就会阻塞直到当前事务提交或回滚。锁的释放依赖事务结束这天然满足了“崩溃自动释放”的要求——只要事务超时回滚锁就没了。乐观锁的做法则是用版本号字段UPDATE t_lock SET version version 1 WHERE resource_id order_123 AND version 1;通过“受影响行数为 1”来判断是否抢锁成功。这种方式没有阻塞严格来说更像是一种 CAS 操作而不是传统的互斥锁。数据库方案的优点非常明显简单、可靠、无额外组件。但也有几个突出的硬伤。第一是性能瓶颈数据库的并发处理能力远低于缓存型中间件在 TPS 上万的高并发场景下很快会成为系统瓶颈。第二是单点风险虽然数据库本身有主从和集群但在锁语义下主从切换时锁状态的一致性很难保证。第三是死锁和阻塞管理悲观锁长时间持有时其他请求排队等待数据库连接池很容易被打满。我的建议是数据库方案适合低并发、逻辑简单的内部系统或者管理后台。高并发核心链路尽量不要碰它。2.2 Redis 方案高并发场景的事实标准Redis 分布式锁是目前互联网行业应用最广泛的方案。核心原因就两个字快。Redis 的纯内存操作单线程模型加上简单的命令语义让它在高并发场景下能保持极高的吞吐和极低的延迟。最基础的实现是两条命令配合使用SET lock_key unique_value NX PX 30000其中NX表示只有 key 不存在时才设置成功PX 30000表示 30 秒后自动过期。释放锁的时候不是简单DEL而是要先用 Lua 脚本比对 value 是不是自己的防止删掉别人的锁if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这个最简单的版本在高并发下存在几个问题。一是锁的过期时间难设定——设短了业务没执行完锁就自动过期了设长了客户端崩溃后锁要很久才能被新请求获取。二是主从切换时有锁丢失风险Redis 主节点刚写入锁还没来得及同步到从节点就挂了哨兵把从节点提升为主节点新主节点上没有这把锁别人就能再次获取同一把锁。为了解决这些痛点业界出现了 Redisson 这类客户端库用“看门狗”机制做自动续期把锁的有效期动态延长。后面第四章我会专门展开讲实现原理。至于 Redlock 算法它在理论上追求多节点层面的互斥但实际工程中争议很大我自己的实践是不太推荐后面也会细说。2.3 ZooKeeper 方案强一致性的牺牲品ZooKeeper 分布式锁基于它的临时顺序节点EPHEMERAL_SEQUENTIAL机制。原理是这样的所有客户端在同一个锁目录下创建临时顺序节点编号最小的那个节点持有锁其他客户端监听自己前一个节点的删除事件当前一个节点被删除时后一个节点就去检查自己是不是编号最小的如果是就获得锁。这里有几个关键技术点值得展开临时节点意味着客户端会话结束或者断连之后节点会被自动删除锁自动释放这是天然的死锁防护。顺序节点保证了公平性先来先得不会有“饿死”现象。监听机制让客户端在锁释放时能够被及时唤醒不需要轮询。同一条路径下的节点创建时由 ZK 保证全局唯一且有序。这套机制的可靠性非常高因为 ZK 的 Zab 协议保证了集群节点之间的数据强一致性只要客户端会话活着锁就不会丢。但 ZK 方案的代价是性能。创建节点、删除节点、客户端监听事件每一步都有 ZK 集群内部的同步开销P99 延迟通常比 Redis 方案高一个数量级。另外ZK 的客户端会话超时有一个 bug 级别的风险点如果客户端发生长时间 GC 停顿会话超时临时节点被删除锁被释放但业务线程其实还没执行完。虽然有这个风险但在对一致性要求极高的场景下比如分布式事务协调、配置变更、领导选举ZK 方案依然是值得信赖的选择。2.4 etcd 方案云原生时代的后起之秀etcd 是 Kubernetes 的底层存储它的分布式锁实现思路和 ZooKeeper 非常相似都是基于强一致性的分布式协调服务。etcd 提供的锁能力基于三个核心机制Lease租约获取锁时创建一个租约租约有过期时间。如果持有者异常退出不会续租租约到期后锁自动释放。这和 Redis 的过期时间思路一致。Revision修订版本所有操作都会产生一个全局递增的 revision 号客户端可以用它做公平排队。Watch监听客户端监听 key 的变化在锁释放时第一时间感知减少无效轮询。etcd 的锁实现和 ZK 类似要求客户端在持有锁期间持续续租否则锁会被自动释放。它比 ZK 更云原生、更轻量如果你的基础设施已经跑在 Kubernetes 上etcd 通常是最顺理成章的选择。不过它的问题也和 ZK 一样吞吐量不如 Redis且需要独立的 etcd 集群来保证可用性。2.5 方案横向对比速查表维度数据库RedisZooKeeperetcd一致性模型取决于数据库隔离级别AP主从切换有锁丢失风险CP强一致CP强一致性能低极高中等中等死锁防护事务超时过期时间/看门狗临时节点会话超时租约续租锁丢失风险主从切换有风险主从切换有风险低低公平性不支持原生不支持可用方案模拟顺序节点天然公平Revision 自然有序运维成本无额外成本低高需要维护 ZK 集群中K8s 生态下较友好典型场景内部系统、低并发互联网高并发核心链路金融级强一致场景云原生环境这张表只是帮你快速建立一个整体印象真正的选型还要结合业务特点我在第三章给出具体的决策方法。3. 选型决策框架复杂场景下怎么选才不后悔3.1 先看业务对一致性的容忍度我在很多技术讨论里看到大家一上来就争论 Redis 方案和 ZK 方案谁优谁劣这种讨论其实很低效。选型的出发点不是技术先进性而是业务对一致性的容忍度。先问自己一个问题如果这把锁“失效”了两个人同时拿到了锁最坏的结果是什么如果最坏结果是多扣一次库存、多送一张优惠券、产生了资损那这个场景对一致性要求极高建议直接在 ZK 或 etcd 里选。如果最坏结果只是重复推送一条消息、重复执行一次非关键任务那 Redis 方案的成本优势和性能优势就非常突出。很多业务的现实是对一致性的真实要求并没有想象中那么高。做活动、抢红包、秒杀稍微超卖一点用事后对账可以修复这时候用强一致方案反而提高了成本、牺牲了性能。3.2 我自己的选型四步法这个选型框架我在团队内部推过很多次效果还可以分享出来供大家参考第一步评估业务并发量。如果峰值 TPS 不超过一千数据库方案完全够用不用过度设计。如果峰值 TPS 到万级别Redis 方案是首选。只有当单把锁的争抢频率极高并且需要公平性时才需要考虑 ZK/etcd。第二步评估锁失效的后果。用上面说的“最坏结果分析”方法给业务场景做一个容忍度分级。容忍度低的直接排除 Redis容忍度高的可以放心用。第三步评估现有的基础设施。如果团队已经运维着 Redis Cluster使用 Redis 方案几乎零成本。如果基础设施已经全面云原生、K8s 化etcd 是自然选择。不要为了分布式锁去专门引入一套 ZK除非确实需要它的强一致能力。第四步评估团队的技术熟悉度。分布式锁实现不难难的是出了问题后的排查。团队对哪个中间件最熟悉出问题时能最快的定位这也是一个不可忽视的因素。3.3 分场景推荐清单互联网高并发秒杀、库存扣减首选 Redis Redisson。性能高使用简单看门狗机制能解决大部分过期问题。对极端一致性的担忧可以通过后续业务补偿去兜底。分布式任务调度、定时任务防重首选 Redis方案同上。任务场景通常可以容忍锁失效后的重复执行再配合任务表的业务幂等即可。分布式事务协调、领导选举首选 ZK 或 etcd。这类场景对一致性零容忍且本身并发量不高强一致方案的性能劣势可以忽略。分布式配置管理、服务注册发现首选 etcd/ZK。这不是单纯的锁问题而是协调服务的主场。内部管理系统、低并发后台数据库方案。简单可靠不需要额外组件出了问题所有人都看得懂。4. 核心实现细节剖析好的代码和能跑的代码是两回事4.1 Redis 锁的原子性到底怎么保证先回到 Redis 最基础的锁实现。很多初学者会写两条独立的命令来加锁SET lock_key value NX EXPIRE lock_key 30000看起来没问题但实际上这两条命令不是原子的。如果第一条执行成功、第二条因为网络抖动或者 Redis 崩溃没有执行这个 key 就成了永不过期的死锁。正确做法是使用 Redis 2.6.12 之后引入的单命令原子写法把SET和过期时间绑在一起SET lock_key unique_value NX PX 30000释放锁的时候也面临同样的问题。最忌讳的写法是if (redis.get(lock_key) unique_value) { redis.del(lock_key); }这是一个经典的先检查后操作的竞态。虽然检查通过了但下一个瞬间锁过期了另一个线程 B 抢到了同一把锁并写入了新的 value然后线程 A 执行DEL就把 B 的锁误删了。解决方式是用 Lua 脚本把“检查 value 是否一致”和“删除 key”合并成一个原子操作。为什么 Lua 脚本在 Redis 里是原子的因为 Redis 是单线程执行命令的Lua 脚本在执行期间不会被其他命令插入天然具备原子性。4.2 Redisson 看门狗机制续期背后的原理前面提到锁过期时间不好设设短了业务没跑完锁就自动释放设长了客户端崩溃后锁要很长时间才能被其他请求获取。Redisson 的解决方案是看门狗机制。Redisson 获取锁时默认锁的有效期是 30 秒lockWatchdogTimeout参数可配置。但拿到锁之后它并不是不管了而是启动一个后台定时任务每 10 秒即 30 秒的三分之一检查一次锁是否还被当前线程持有如果还在就通过 Lua 脚本把锁的过期时间重置为 30 秒。这个机制的关键在于锁的有效期是动态续期的只要业务线程还在执行锁就不会过期。但要注意看门狗只在当前客户端进程内运行它默认使用的是 JVM 层面起的守护线程不依赖 Redis 服务端。如果客户端进程宕机守护线程随之消失锁在最长 30 秒后自动过期不会造成永久死锁。看门狗这个设计里有几个隐藏的坑业务代码必须通过 Redisson 提供的 API 来调用锁如果自己手写SET NX PX加锁Redisson 的续期逻辑不生效锁还是会被固定过期时间杀掉。看门狗对长时间持有锁的业务无效。如果业务需要持锁超过几十分钟甚至几小时靠续期维持是不现实的需要业务侧自己评估是否有必要长期持锁。超时会导致看门狗失效。如果用了tryLock(long waitTime, TimeUnit unit)这种带等待时间的重载方法Redisson 会使用传入的超时时间作为锁的固定有效期不再启动看门狗。我在项目里常规做法是如果业务能预估执行时间就传入合理的超时时间不使用看门狗如果业务执行时间不可控用不带超时参数的lock()方法让看门狗做动态续期。前提是业务本身要有兜底机制即使锁丢了也不会造成灾难性后果。4.3 ZooKeeper 锁的完整实现逻辑ZK 锁的核心代码逻辑我习惯这样写在锁根节点下创建临时顺序节点比如/locks/order_123/lock_。获取/locks/order_123下的所有子节点排序后找到自己创建的节点。如果自己的节点是序号最小的说明获取锁成功。如果自己不是最小序号监听前一个节点的delete事件一旦前一个节点删除重新执行判断。这里有个非常重要的实现细节监听的是前一个节点而不是根节点或所有子节点。这样做有两个目的。一是避免“惊群效应”herd effect也就是锁释放时所有等待者都被唤醒然后互相竞争。只监听前一个节点锁释放时只会唤醒一个等待者唤醒开销降至最低。二是保证公平性编号次序就是抢锁次序不会出现插队。ZK 锁能防死锁的关键是临时节点。客户端与 ZK 服务端之间的会话是长连接如果客户端崩溃或网络断开会话超时后 ZK 会自动删除所有以该会话创建的临时节点这就实现了“锁随会话死亡而释放”。但与 Redis 相比ZK 锁有另一个角度的陷阱会话超时时间的设置。如果超时时间设得太短客户端 GC 停顿这类的短暂不可用会被误判为会话失效锁被提前释放如果设得太长客户端真的崩溃后其他客户端要等待很久才能获取锁。实践中建议结合业务的 GC 停顿监控数据来设置通常设置在 5 到 10 秒之间并且要确保应用容器的 GC Pause 时间远小于这个值。4.4 可重入锁怎么实现单机环境里的synchronized天然支持可重入同一个线程可以多次获取同一把锁。但分布式锁默认是不支持可重入的因为服务端并不知道多次加锁的是不是同一个客户端。Redis 方案实现可重入有两种思路。一是用 Redisson它默认基于 Hash 数据结构实现可重入结构是hash : { threadId : count }每次重入 count 加一每次释放 count 减一减到零才删除 key。二是自己实现在业务里维护一个本地 ThreadLocal 记录当前线程的锁次数加锁时如果发现已有锁是自己持有的就直接放行。ZK 方案的可重入实现麻烦一些因为 ZK 的节点是持久化的路径没有“持有者计数”的概念。我的做法是在锁被获取时把客户端标识比如host:threadId写入节点数据data再次加锁时先检查节点数据是不是自己是则重入否则等待。但要小心ZK 节点数据是有大小限制的默认 1MB这个量级对存储线程标识来说绰绰有余。其实我建议高并发场景优先考虑 Redisson 的可重入实现成熟稳定不用重复造轮子。自己实现可重入时最容易出 bug 的地方是锁计数没有正确清理导致锁一直无法释放形成死锁排查起来相当痛苦。5. 生产环境高频踩坑与排查实录5.1 锁过期了业务却没执行完这是 Redis 方案上线后最常遇到的线上问题。表现是业务突然出现并发冲突明明加了锁数据还是被重复处理了。排查后大概率是锁过期时间设置得太短业务执行时间超过了锁的有效期。我遇到过的一个具体案例一个库存同步任务正常执行时间在 2 秒以内就设置了 10 秒的超时时间。结果某天数据库慢查询业务执行了 15 秒。第 10 秒锁自动过期另一个实例马上拿锁执行同一任务两个任务同时操作同一批库存出现了超卖。这类问题的根治思路是在业务层面对“锁被提前释放”做兜底。比如在业务执行前判断锁是否还在或者用 Redisson 的看门狗做续期。但更关键的是要意识到分布式锁中间的“分布式”三个字意味着锁的有效期不可能完全覆盖业务执行时间你永远要假设锁可能会丢。在这个假设下做幂等设计才是高并发系统的正确姿态。5.2 主从切换导致锁丢失Redis 主从架构下锁丢失的场景是这样的客户端 A 在主节点上写入锁 key主节点的数据还没同步到从节点主节点挂了。哨兵把从节点提升为主节点此时新主节点上没有这把锁。客户端 B 尝试获取同一把锁成功了。这样 A 和 B 同时持有了锁互斥性被打破。为什么 Redlock 算法在这种情况下被提出它的思路是不依赖单个 Redis 节点而是向集群中多个独立的 Redis 节点同时写锁只有过半节点写入成功才算拿到锁。这样即使某个主从切换导致部分节点丢了锁只要大多数节点上还有锁整体上仍然能保证互斥。但这个算法在工程界争议非常大包括 Redis 作者和很多一线工程师都发表过反对意见。核心原因是分布式系统里“网络分区”时Redlock 无法同时保证安全性和可用性。我个人的实践结论是如果业务对锁丢失零容忍不应该用 Redis 方案直接上 ZK 或 etcd如果业务能容忍极小概率的锁丢失直接用 Redis加业务幂等兜底比用 Redlock 划算得多。5.3 锁误删大家都犯过的低级错误锁误删问题在前面 4.1 已经提过原理这里再讲一个真实的教训。有一次我们排查线上问题发现某个热点商品的库存扣减偶发出现超卖但概率极低。查了很久最后定位到是一位同事在释放锁时只做了简单的DEL操作没有比对持有者标识。触发路径是这样的线程 A 拿锁设置超时 5 秒。A 业务执行了 4.9 秒锁只剩 0.1 秒。这时候刚好发生了一次 Full GCA 被暂停了 1 秒。锁在 A 暂停期间过期。线程 B 获取锁开始执行业务。A GC 结束继续执行完业务然后执行DEL把 B 的锁删了。线程 C 此时也来抢锁成功拿到锁于是 B 和 C 同时执行业务。这是一个非常经典的“GC 暂停导致锁过期 误删锁”的组合场景。理解这个问题的关键是GC 停顿会让线程的“现实时间”和“逻辑时间”脱节。锁的过期时间是基于现实时间计算的而线程执行进度是基于逻辑时间推进的。解决方式就是前面反复强调的释放锁时一定要用 Lua 脚本做“先校验持有者再删除”的原子操作。5.4 惊群效应与连接风暴ZK 方案中如果实现不当容易出现惊群效应。假设有 100 个客户端在等待同一把锁如果每个客户端都监听了锁节点的删除事件锁一释放100 个客户端同时被唤醒同时去竞争创建锁节点。这不仅会造成瞬时大量 ZK 请求增大节点压力还会导致大量客户端同时抢锁造成不必要的网络开销和 CPU 消耗。正确做法是只监听前一个节点。如果业务对线程唤醒的实时性要求极高还可以考虑用 etcd 的Watch机制它天然支持只观察前一个 revision实现方式更优雅。另外我在 ZK 方案里还会加一个保护机制客户端被唤醒后不立即抢锁而是随机加一个 50 到 200 毫秒的小抖动错峰执行进一步降低瞬时冲击。5.5 常见问题速查表异常现象可能原因排查方向解决方案加了锁还是超卖锁过期/锁丢失/锁误删查看锁大 key 历史、开启慢查询、检查释放锁逻辑用 Lua 释放锁看门狗续期业务幂等兜底获取锁超时网络抖动/Redis 负载高查看 Redis 的 INFO 命令和慢日志增加重试机制使用连接池优化 Redis 参数锁一直无法释放业务线程死循环/看门狗失效dump 线程栈检查 Redisson 版本配置使用 tryLock 带超时兜底释放逻辑锁被提前释放GC 停顿/网络分区查看 GC 日志查看 Redis 客户端重连日志合理设置超时时间考虑 ZK/etcd 方案大量请求同时阻塞惊群效应/不合理的监听策略查看中间件连接数监控改为只监听前驱节点加随机抖动ZK 会话频繁过期会话超时时间设置过短查看客户端 GC 日志和网络状况调大会话超时时间降低 ZK 心跳频率这张表是我做了多年分布式系统之后沉淀出来的高频问题集合。实际排查时建议配合监控系统记录锁的获取耗时、持锁时间、释放耗时三个核心指标这样一旦出现异常可以直接定位是获取慢、持锁太久还是释放失败。6. 一个完整的 Redisson 接入示例选型部分讲了很多理论我最后给一个可以直接抄作业的 Redisson 接入示例这样你可以更快地在项目里落地。首先引入依赖Maven 为例dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.20.1/version /dependency然后在application.yml中配置连接spring: redis: redisson: config: | singleServerConfig: address: redis://127.0.0.1:6379 connectionMinimumIdleSize: 10 connectionPoolSize: 50这里要注意的是connectionPoolSize不能设置太小否则高并发下容易出现获取连接超时。我见过很多项目用默认的 8 个连接结果一到秒杀高峰期就报Cannot get a connection最后都是把连接池调大才解决。核心业务代码Autowired private RedissonClient redissonClient; public void doBiz(String bizKey) { String lockKey lock:order: bizKey; RLock lock redissonClient.getLock(lockKey); try { // 尝试获取锁等待 3 秒锁自动过期时间由看门狗维护 if (lock.tryLock(3, TimeUnit.SECONDS)) { // 业务逻辑 } else { throw new BizException(系统繁忙请稍后重试); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new BizException(获取锁被中断, e); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } }这里有几个细节值得说明lock.isHeldByCurrentThread()非常重要。如果直接调用lock.unlock()在锁已经过期被其他线程获取的情况下会抛出IllegalMonitorStateException。有了这个判断可以避免不必要的异常。tryLock(3, TimeUnit.SECONDS)的第一个参数是获取锁的等待时间。如果超过 3 秒还没有拿到锁直接返回 false。这个参数要结合业务的执行时间设置不要让大量请求在锁上排队太久。unlock如果放在finally块里要注意它本身可能是会抛异常的网络操作。实际生产中释放锁失败会影响后续流程所以要么捕获处理要么让异常抛出后由全局异常处理兜底。如果是多实例部署的环境要确保每个实例的RedissonClient连接的是同一个 Redis 集群。我曾经遇到过一个低级错误测试环境只部署了一个实例连的是 Redis 单节点一切正常。上线后多实例部署有人写死了 IP 连到了同一台 Redis但网络策略只放行了其中一个实例导致另外几个实例根本拿不到锁。这种问题排查起来很隐蔽基本只能靠看日志发现连接异常。最后再说两句大实话分布式锁看着简单用起来处处是坑。我最大的体会是不要在方案上过度纠结要在业务兜底上多花心思。任何分布式锁方案都不能保证 100% 的互斥性与其追求绝对的强一致不如假设锁一定会丢把后续的幂等性设计做到位。真能做到这一步你在高并发系统设计上就已经跨过一道坎了。还有一个最后的建议如果团队不熟悉 ZK/etcd 的运维优先选择 Redis 方案但一定要用成熟的开源客户端不要自己从底层去撸造轮子。分布式锁这个领域坑已经被踩得很透了站在别人的肩膀上会省去很多踩坑的时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价