明明接口幂等就能防重复提交,为什么还要加分布式锁?
这个问题挺有代表性的我在做 Code Review 的时候也见过不少人把这两个东西搞混觉得做了幂等就万事大吉了分布式锁是多余的。幂等和分布式锁解决的根本不是同一个问题。幂等管的是结果正确性分布式锁管的是并发执行顺序。两者是互补关系不是替代关系。幂等到底在防什么幂等的定义很简单同一个请求执行一次和执行多次产生的效果一样。最常见的实现方式就是幂等 Token前端提交前先请求一个唯一 Token提交时带上这个 Token后端收到后先查这个 Token 是否已经被消费过消费过就直接返回没消费过就执行业务逻辑。public String submitOrder(String idempotentToken, OrderRequest request) { // 1. 检查 Token 是否已消费 boolean exists redis.hasKey(idempotent: idempotentToken); if (exists) { return 重复提交请勿重复操作; } // 2. 标记 Token 已消费 redis.opsForValue().set(idempotent: idempotentToken, 1, 30, TimeUnit.MINUTES); // 3. 执行业务逻辑 orderService.createOrder(request); return 下单成功; }看起来没毛病吧Token 用过了就标记第二次来直接拒绝完美防重复提交。问题出在哪上面这段代码在低并发下确实没问题。但线上环境用户手抖双击提交按钮或者前端没做防抖两个请求间隔可能只有几十毫秒几乎同时到达后端。画个时间线你就明白了两个请求都在 T1、T2 时刻检查 Token此时 Token 都还没被标记所以都通过了检查最终两个请求都执行了下单逻辑一笔订单变成了两笔。这就是经典的检查-执行竞态条件。幂等检查和业务执行之间有一个时间窗口并发请求可以同时穿过这个窗口。用 Redis 原子操作能解决有人说把检查和标记合成一个原子操作不就行了public String submitOrder(String idempotentToken, OrderRequest request) { // SETNX不存在才设置原子操作 Boolean success redis.opsForValue() .setIfAbsent(idempotent: idempotentToken, 1, 30, TimeUnit.MINUTES); if (!success) { return 重复提交请勿重复操作; } // 执行业务逻辑 orderService.createOrder(request); return 下单成功; }用SETNX确实能保证只有一个请求能设置成功解决了并发下 Token 检查的竞态问题。在防重复提交这个简单场景下SETNX 确实够用了。但这就等于你已经在用分布式锁的思路了SETNX 本质上就是一个最简版的分布式锁只不过你没显式地叫它锁而已。那为什么实际项目中还是要单独引入分布式锁呢因为真实业务场景比防重复提交复杂得多。场景一业务逻辑本身有并发互斥需求看一个扣库存的场景public void deductStock(String skuId, int quantity) { // 1. 查当前库存 int stock stockMapper.getStock(skuId); // 2. 判断库存是否充足 if (stock quantity) { throw new BizException(库存不足); } // 3. 扣减库存 stockMapper.deductStock(skuId, quantity); }这里根本没有幂等 Token 的概念每个用户下的都是不同的订单、不同的请求幂等校验全部放行。但 100 个用户同时买最后 1 件商品如果没有锁来控制并发就会出现超卖。幂等解决的是同一个操作别执行两次而这里的问题是不同的操作在并发修改同一份数据这是幂等覆盖不到的。加上分布式锁public void deductStock(String skuId, int quantity) { RLock lock redisson.getLock(lock:stock: skuId); lock.lock(); try { int stock stockMapper.getStock(skuId); if (stock quantity) { throw new BizException(库存不足); } stockMapper.deductStock(skuId, quantity); } finally { lock.unlock(); } }锁保证了同一时刻只有一个线程能进入 查库存 - 判断 - 扣减 这个完整的流程从根本上杜绝了并发读写数据不一致的问题。场景二幂等挡不住并发下的数据错乱再看一个转账的场景A 给 B 转 100 块同时 C 也给 B 转 200 块。public void transfer(String from, String to, BigDecimal amount) { // 1. 查余额 BigDecimal balance accountMapper.getBalance(to); // 2. 加钱 accountMapper.updateBalance(to, balance.add(amount)); }两笔转账是完全不同的业务操作幂等 Token 不会拦截任何一笔。但并发执行的时候两笔转账都完成了但 B 的余额应该是 1300实际却是 1200100 块钱凭空消失了。这就是典型的并发写覆盖问题幂等完全无能为力必须用锁来串行化对同一账户的操作。场景三分布式 synchronized 不够用有人说加个synchronized就行了在单机环境下确实可以public synchronized void deductStock(String skuId, int quantity) { // ... }但微服务架构下同一个服务部署了 3 台机器synchronized只能锁住当前 JVM 内的线程。请求被 Nginx 分发到不同的机器上3 台机器上的 3 个线程各自锁各自的等于没锁。三个节点的锁互相不可见三个请求同时操作同一份数据并发问题照样出现。分布式锁就是为了解决跨 JVM 的互斥问题让多台机器像在一个 JVM 里一样排队执行。分布式锁 幂等一个线上靠谱的下单接口应该是这样的public String submitOrder(String idempotentToken, OrderRequest request) { // 第一层分布式锁 - 控制并发同一时刻只允许一个请求进入 RLock lock redisson.getLock(lock:order: idempotentToken); boolean acquired lock.tryLock(3, 10, TimeUnit.SECONDS); if (!acquired) { return系统繁忙请稍后重试; } try { // 第二层幂等校验 - 锁内检查保证不会重复执行 Boolean firstTime redis.opsForValue() .setIfAbsent(idempotent: idempotentToken, 1, 30, TimeUnit.MINUTES); if (!firstTime) { return订单已提交请勿重复操作; } // 执行业务 orderService.createOrder(request); return下单成功; } finally { lock.unlock(); } }为什么锁内还要做幂等因为分布式锁是有过期时间的。假设锁的过期时间是 10 秒业务逻辑因为一次慢 SQL 执行了 12 秒锁在第 10 秒自动释放了这时候如果有一个重试请求进来拿到了锁没有幂等校验的话就会重复执行。分布式锁防的是并发幂等防的是重复一个管时间窗口一个管最终结果。说在最后回到最开始的问题有了幂等为什么还要分布式锁因为幂等的前提是能正确判断这个操作是否已经执行过而在高并发场景下如果没有锁的保护连这个判断本身都可能出错。幂等是对结果负责的不管你调几次效果一样。分布式锁是对过程的约束同一时刻只能有一个人在操作。一个管结果一个管过程缺一不可。

相关新闻

2026年个人远程控制软件盘点:哪款才是你的居家办公最佳搭档?

2026年个人远程控制软件盘点:哪款才是你的居家办公最佳搭档?

远程控制早已不是企业IT运维的专属工具。下班路上临时调取家中电脑的文件、出差时远程操作办公室的台式机、帮远在老家的父母解决手机卡顿——这些日常场景让远程控制软件走进了每个人的生活。但市面上的远控工具五花八门,有的连上就卡顿掉线,有的免费版…

2026/11/1 17:19:49 阅读更多 →
从循环工程到原生模型切换:Codex与Claude Code的Agent调优实践

从循环工程到原生模型切换:Codex与Claude Code的Agent调优实践

如果你最近频繁使用 Codex 或 Claude Code,大概率撞到过一个“看起来很努力、实际上原地打转”的 Agent 场景:它反复修改同一个文件,测试在红绿之间来回跳,日志刷了一批又一批,但问题始终没有收敛。很多人第一反应是“…

2026/11/1 10:32:09 阅读更多 →
在8美元ESP32-S3上训练小语言模型:端侧AI训练实战解析

在8美元ESP32-S3上训练小语言模型:端侧AI训练实战解析

这次我们来看一个很特殊的项目:An SLM trained on $8 ESP32-S3。标题翻译过来是“在 8 美元的 ESP32-S3 上训练一个小语言模型”。光看这个名字,基本就能判断它的定位:不是拿 ESP32-S3 去跑大模型推理,而是把语言模型的训练这件事…

2026/10/25 20:40:54 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/1 0:24:43 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/1 10:36:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/1 10:36:51 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/1 1:06:58 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/1 10:35:11 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/1 9:35:41 阅读更多 →

日新闻

周新闻

月新闻