新闻资讯

记录优翰动态,分享技术实践。

多标签页同时提交导致重复下单:锁与会话共享的排查清单

同一个账号在浏览器里开多个标签页,先后点击下单或提交表单,后台出现了两笔订单或两条业务记录;另一种表现是,第一个请求还没返回,第二个请求已经提示超时,用户重试后反而更容易出错。问题往往不在单个页面的代码,而在会话共享、前端防重与后端幂等三件事没有对齐。

下面的内容按「现象 → 可能原因 → 排查顺序 → 预防措施」展开,适合 B/S 架构的管理系统和电商类下单流程参考。如果你正在规划类似的改造,可以先看看我们的软件定制开发与系统集成服务,本文只讨论设计与验收方法。

浏览器多标签页共享会话会带来什么差异

同一浏览器下,多个标签页通常共享同一份 Cookie 或同一份本地存储里的登录凭据。也就是说,两个标签页看到的「当前用户」和「会话状态」是一致的,但页面内的按钮状态、表单数据、请求进行中标记各自独立。

由此产生的典型差异有两点。一是用户在 A 标签页点了提交,B 标签页的按钮并不会自动置灰;二是 A 标签页的请求等待超时后,用户切到 B 标签页再提交,业务系统收到的是两个源自同一会话的请求。

常见的可能原因包括:

  • 前端只做了按钮点击防抖或置灰,没有覆盖另一个标签页。
  • 会话中用同一个键保存「提交中」状态,结果被后一个页面覆盖。
  • 后端把请求当作完全独立的新增处理,没有幂等标识。
  • 订单号在请求时才生成,两个请求各自生成了不同单号。

排查顺序:从前端防重到后端唯一约束

不同层面的措施成本不同,建议按由浅入深的顺序排查,每一步留下结论再进入下一步。

  1. 先确认复现路径:是同一按钮连点,还是两个标签页各提交一次,还是用户等待超时后重试。这三类应分开处理。
  2. 检查前端是否只在当前页面内防重,比如按钮置灰、请求中状态;再检查这些状态是否只在内存里,刷新或切换标签页即失效。
  3. 检查请求是否携带稳定的业务幂等键,比如由业务单据号、用户与操作类型组合生成,而不是时间戳或随机数。
  4. 检查后端是否有针对幂等键的去重判断,去重范围是否覆盖并发,而不仅是顺序请求。
  5. 检查数据库层面是否有唯一约束或版本号字段作为最后防线,确认约束字段与业务含义一致。
  6. 最后检查锁的粒度与超时设置,确认超时释放后不会留下悬挂状态,也不会让用户长时间无法操作。

排在后面的步骤不代表更重要。很多项目的问题出在第二步:前端看着做了防重,实际只在单个标签页内有效。

幂等键与唯一约束怎么分工

幂等键解决「相同操作重复到达」的问题,唯一约束解决「并发写入同一业务单据」的问题。两者结合,才能同时覆盖重复点击与多标签页并发两种场景。

实践中建议把幂等键设计成业务可解释的字符串,例如从页面进入时由后端下发、随表单一同提交的单据标识。这样同一份表单重复提交时会命中同一条幂等记录,用户看到的是同一个结果,而不是第二笔订单。

  • 幂等记录需要落库或写入带过期时间的缓存,仅存在应用内存里的记录在扩容后会失效。
  • 数据库唯一约束应落在业务真正唯一的字段组合上,不要为了去重单独造一个不参与业务的字段。
  • 冲突时应返回明确的业务提示和已有单据信息,便于用户确认,而不是笼统的报错。

接口层面的通用做法,我们在更多技术实现细节里也有整理,可以与本文的排查顺序对照使用。

加锁还是靠数据库兜底:超时锁的取舍

分布式锁能减少无效请求进入核心逻辑,但它不是必须项,也不应成为唯一防线。判断依据主要有几条:业务写入是否集中在同一资源、并发是否会造成真实损失、系统能否接受短暂的重试等待。

锁的设计要注意三点。第一是锁的键必须与业务资源绑定,不能用一个全局键把无关请求全部串行化;第二是设置合理的超时时间,超时过短会让长事务被重复进入,超长会让用户操作受阻;第三是释放时要确认持有者身份,避免误删他人的锁。

锁是降低冲突概率的手段,唯一约束才是保证数据不重复的底线。两者不应互为替代。

当业务写入相对简单时,也可以不加锁,直接依赖数据库唯一约束并配合重试提示,架构上更轻。是否引入锁,最终要看一致性与并发成本的取舍,并写进设计说明。

表单状态与会话共享的设计细节

前端可以通过本地存储或跨标签页通信,把一个标签页进入提交状态的通知同步到其他页面。这种方式实现成本不高,能让用户直观看到「另一个页面正在提交」,从而减少重复操作。

需要注意,前端同步只是一种提示,不能当作安全保障。用户禁用脚本、刷新页面、换用其他设备,都可能绕过提示。真正决定结果在服务端。

如果系统里存在类似流程,也可以参考我们做过的互联网与软件行业案例中的场景划分,再结合各自业务量级定方案。

上线前的自查清单

下面这份清单可以直接拿去核对,也可作为验收时的对照项。具体做法的取舍与实施步骤,可参考服务范围与交付流程。

  • 同一账号在两个标签页分别提交同一份表单,是否只产生一条业务记录?
  • 请求超时后用户重试,是否返回原结果而非新增单据?
  • 幂等键是否由后端下发并随表单一同提交,而不是前端临时生成?
  • 幂等记录是否落在数据库或有持久化保证的存储中?
  • 数据库唯一约束是否覆盖了真正的业务唯一条件?
  • 发生并发冲突时,提示信息是否说明已有单据,用户能否继续操作?
  • 锁的超时时间是否经过评估并写入设计文档?
  • 提交过程中的页面提示,是否能让用户判断「正在处理」还是「已失败」?

常见问题

加锁之后还会重复下单吗

仍有可能,例如锁过期后第二个请求进入,或者加锁范围没有覆盖完整写入路径。因此建议保留数据库唯一约束作为最后防线。

只有 APP 端要做这些处理吗

不是。浏览器多标签页、APP 多次点击、接口被重复调用,本质都是同一请求重复到达,处理思路可以共用一套幂等设计。

幂等键和订单号是什么关系

两者可以不同。业务上通常要在表单进入时就确定一个提交标识,订单号则可能在成功后生成;设计时要避免用一个每次请求都变化的标识去做去重依据。

@2016-2026 上海优翰信息科技有限公司 版权所有
沪ICP备16045124-1号
沪公网安备 31011202014563号

沪公网安备 31011202014563号