news 2026/5/4 11:18:28

拼多多二面挂了!问 “支付接口怎么防重”,我答“先查后写”,面试官让我出门左转。

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
拼多多二面挂了!问 “支付接口怎么防重”,我答“先查后写”,面试官让我出门左转。

昨晚有个做电商的粉丝找我哭诉,说在拼多多二面被虐得体无完肤。

面试官问了一个这行最显基本功的问题:“用户手抖,连续点击了两次‘支付’按钮,或者网络抖动导致前端重发了请求,你的后端接口怎么保证不扣用户两笔钱?

这兄弟觉得送分题来了,自信满满地说:“简单啊,进业务逻辑前,先去数据库查一下订单状态。如果已经是‘已支付’,就直接返回成功;如果是‘未支付’,再执行扣款逻辑。”

面试官听完,把简历一合,冷冷地说:“并发懂吗?两个请求在毫秒级同时到达,你的 Select 查出来的都是‘未支付’,然后两个线程同时执行扣款。恭喜你,刚上线就造成了资金损失,这是 P0 级事故,公司把你卖了都赔不起。”

兄弟当场愣住,支支吾吾说不出话,最后直接挂了。

说实话,“幂等性(Idempotency)” 是分布式系统最核心的底裤。如果连这层底裤都守不住,你的系统就是在裸奔。 今天带你拆解这道题的 3 种段位,看看你是青铜还是王者。

一、 致命盲区:“先查后写”为什么是自杀?

为什么面试官听到“先查后写(Check-Then-Act)”会发飙?

因为在高并发下,数据库的读写是有时间差的。即使前端做了按钮置灰和防抖,也只能防君子防不住“网络抖动”或“黑客抓包重放”。

场景还原:

  1. 线程 A进来,查订单 1001,发现是“未支付”。

  2. 线程 B刚好也进来,查订单 1001,发现也是“未支付”。

  3. 线程 A执行扣款,把状态改为“已支付”。

  4. 线程 B继续执行扣款,再次扣钱,覆盖状态。

结论: 在并发场景下,应用层的 if (status == Unpaid) 判断形同虚设。只要没有底层的原子性互斥机制,这种代码上线就是 Bug。

二、 核心架构:3 种主流解法(从青铜到王者)

解法 1:数据库唯一索引(硬核兜底)

这是最简单粗暴,但最有效的保命手段。

逻辑: 在数据库里建一张“流水表(Payment_Log)”,把 order_id 或者 全局唯一流水号 建为Unique Key(唯一索引)

流程:

  1. 请求来了,先往流水表插数据。

  2. 如果插成功了,说明是第一次请求,继续跑业务。

  3. 如果报了 DuplicateKeyException(主键冲突),说明是重复请求,直接 Catch 异常,返回“支付成功”。

Fox 点评: 这招好用,但有短板。如果你的业务分库分表了,或者数据库本身就是性能瓶颈,频繁利用数据库的异常机制来控制业务流程,吃相有点难看,且对 DB 压力大。但作为最终防线,它非常可靠。

解法 2:Redis Token 机制(经典的 Web 方案)

这是很多中大厂的标准做法,主要防用户手抖,由前端配合。

流程:

  1. 第一阶段: 用户进入收银台页面,前端先调后端接口获取一个全局唯一的 Token,存入 Redis。

  2. 第二阶段: 用户点“支付”时,把这个 Token 带在 Header 里传给后端。

  3. 后端校验: 后端拿到 Token,去 Redis 删掉这个 Key。

  • 如果删除成功(返回 1),说明是第一次,放行。

  • 如果删除失败(返回 0),说明已经用过了,直接拦截。

⚠️ 高阶防坑点(面试必问):

“是先执行业务再删 Token,还是先删 Token 再执行业务?”

满分回答: “必须先删 Token! 这叫‘先斩后奏’。 如果你先跑业务,业务跑了一半网络断了,Token 还在,用户重试时就又穿透了。

当然,先删 Token 有个小问题:如果业务跑失败了,Token 也没了,用户想重试怎么办?简单的解法是:后端返回特定错误码,前端捕获后自动申请一个新的 Token 再重试。”

解法 3:状态机 + 乐观锁(架构师级解法)

如果不想引入 Redis 增加架构复杂度,也不想单纯依赖数据库唯一索引报错,SQL 乐观锁 是最优雅的方案。 不要查出来在内存里判断,而是把判断逻辑下沉到 Update 语句中。

错误写法:

UPDATE orders SET status = 'PAID' WHERE id = 1001;

王者写法(CAS 机制):

UPDATE orders SET status = 'PAID' WHERE id = 1001 AND status = 'UNPAID'; -- 关键在这里!

逻辑:

利用数据库行锁的原子性。只有当当前状态真的是“未支付”时,这条 SQL 才会执行成功(Affected Rows = 1)。

如果第二个请求过来,发现状态已经是 PAID 了,Where 条件不满足,Affected Rows = 0。

后端代码判断:if (updateCount > 0) return Success; else return "重复请求";

Fox 点评: 这招叫“四两拨千斤”。利用数据本身的版本号或状态做天然栅栏,不需要引入任何中间件,代码极其干净。

三、 最后的“防杠”指南(扫清死角)

设计完方案,面试官一定会追问死角,这 4 个回答能帮你拿 SSP:

Q1:前端已经做了防抖和按钮置灰,后端还要做这么重吗?

: “必须做。前端防君子,后端防小人。 前端防抖防不住网络抖动导致的网关重试,更防不住黑客直接抓包重放。前端只是为了提升用户体验(减少报错),后端的 DB 锁和状态机才是保障数据一致性的底线。”

Q2:如果业务逻辑超级复杂,DB 抗不住怎么办?

: “上分布式锁(Redis Redisson)。 对于高并发写场景,在 Service 层加分布式锁(按 order_id 锁)。 但要注意:锁的粒度要细,必须设置超时时间防止死锁。而且,分布式锁只能‘挡流量’,数据库层的唯一索引兜底永远不能丢(防止锁失效的极端情况)。”

Q3:状态机方案中,如果我要调银行接口,超时了怎么办?

答: “这时候不能只有 UNPAID 和 PAID。要引入中间态 PAYING(支付中)。 先原子更新为 PAYING,然后调银行接口。如果重复请求进来看到是 PAYING,系统应返回‘处理中,请稍候’,避免重复调用银行。”

Q4:重复请求到底该返回什么?

: “一定要区分‘业务状态’

如果订单已支付:直接返回‘支付成功’(结果幂等)。

如果订单处理中:必须返回‘处理中’(Pending)。

如果订单未支付:执行正常扣款逻辑。 千万不能不管三七二十一都返回‘成功’,否则上游会误判。”

四、 面试标准答案模板(建议背诵)

下次被问到“接口幂等性”或“防重”,直接按这个套路输出:

“对于支付这种核心链路,简单的‘先查后写’是绝对不安全的。我的设计思路是 ‘多层防御,DB 兜底’:

体验层(Token): 利用 Redis Token 机制,先删后写,拦截掉大部分用户层面的重复点击和误操作。

核心层(状态机): 数据库层面使用CAS 乐观锁(Update where status=Unpaid),利用 Row Lock 保证并发下的原子更新。引入分布式锁应对极端并发。

兜底层(唯一索引): 在流水表建立全局唯一索引,这是物理层面的最终保险,确保数据绝对一致。

交互设计: 严格区分‘支付中’和‘支付成功’的返回值,确保上游系统逻辑正确。”

写在最后

技术面试考的不是你会写多少 if-else,而是你对“数据安全”有没有敬畏之心。 能用数据库行锁解决的,别搞复杂的分布式锁;能用状态机解决的,别强依赖 Redis。 这套Token 防手抖 + 状态机防并发 + 索引兜底的组合拳,就是架构师的标准答案!

https://mp.weixin.qq.com/s/_YipzFob28Y_oQtLKCw39A

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 3:48:52

微信小程序的自助洗衣房洗衣机预约系统

文章目录微信小程序自助洗衣房预约系统摘要主要技术与实现手段系统设计与实现的思路系统设计方法java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!微信小程序自助洗衣房预约系统摘要 该系统基于微信小程序平台开…

作者头像 李华
网站建设 2026/4/18 18:49:54

广告创意自动化:lora-scripts赋能营销团队批量产出视觉素材

广告创意自动化:lora-scripts赋能营销团队批量产出视觉素材 在品牌营销的战场上,时间就是流量,创意就是武器。可现实是,每一轮广告投放背后,都是一场与人力、周期和预算的拉锯战——设计师通宵改图、文案反复打磨、A/B…

作者头像 李华
网站建设 2026/4/27 12:09:03

我的创作纪念日 2023-》2026

我的创作纪念日 2023-》2026 文章目录 我的创作纪念日 2023-》2026编程三载:从 2023 到 2026,在代码世界里慢慢生长2023:在 "踩坑" 中搭建地基2024:在 "深耕" 中突破瓶颈2025:在 "实践"…

作者头像 李华
网站建设 2026/5/1 7:46:50

【智能体】如何做一个教程写作智能体?

要实现一个教程写作智能体(AI agent that generates tutorials),有两种主要路径:无代码/低代码平台(快速上手,适合初学者)和代码实现(更灵活、可定制,适合开发者&#xf…

作者头像 李华
网站建设 2026/5/2 17:41:59

心理健康关怀项目:艺术家与心理学家合作开发治愈系AI画作

心理健康关怀项目:艺术家与心理学家合作开发治愈系AI画作 在城市节奏日益加快的今天,焦虑、孤独和情绪波动已成为许多人日常生活中的隐性负担。传统心理干预手段如心理咨询、艺术治疗虽有效,却受限于专业资源稀缺、服务成本高以及可及性不足的…

作者头像 李华
网站建设 2026/4/29 8:15:38

海外华人创业机会:为中国客户提供lora-scripts远程技术支持

海外华人创业机会:为中国客户提供 LoRA 远程技术支持 在生成式 AI 爆发的今天,越来越多中国企业开始尝试将 Stable Diffusion 和大语言模型(LLM)融入产品与运营。但现实是:大多数团队卡在“最后一公里”——他们买得起…

作者头像 李华