首页 新闻 会员 周边

电商交易系统的核心设计哲学

0
[已关闭问题] 关闭于 2026-02-25 17:33
理清三点:
1、我点击购买,付款的时候,点击付款界面弹出框❌,关掉付款界面,变成待付款,这时候,商家这边库存会减1,为什么不是付款成功再减1
2、变成待付款,TTL 15分钟后,为什么不会消费者自动关掉待付款,而是超时后如果没有转给死信队列,还是会显示待付款
3、数据库状态为什么会变成待支付,难道是关掉付款界面就变成待支付状态
*Tesla*的主页 *Tesla* | 小虾三级 | 园豆:1516
提问于:2026-02-25 17:33
< >
分享
所有回答(1)
0

1️⃣ 为什么“待付款”就减库存,而不是“付款成功”再减?

这是一个经典的“库存扣减时机”问题。电商界主要有两种策略:

A. 下单减库存(拼多多、淘宝、京东的主流模式)✅

  • 动作:用户点击“提交订单”(生成订单号),立刻扣减库存。此时状态是待支付
  • 为什么这么做?
    • 防止超卖:这是最核心的原因。假设只有 1 台 iPhone,100 个人同时点击购买。如果等到“支付成功”才减库存,那么这 100 个人都能完成支付(因为支付时库存还是 1)。结果就是:1 台手机卖给了 100 个人。商家发不出货,会面临巨额赔偿和信誉破产。
    • 用户体验:用户付完款,发现没货了要退款,体验极差。不如在下单时就告诉他“没货了”,或者锁定给他。
  • 代价:会有“库存被占用但未成交”的情况(比如你刚才说的,关了支付窗口不买了)。这就需要“超时自动取消 + 回滚库存”机制来补救。

B. 支付减库存(某些秒杀活动或特殊商品)

  • 动作:下单时不减库存,只预占一个“名额”。等银行回调说“钱到了”,才真正扣减库存。
  • 缺点:极易超卖。通常只用于库存极大、或者允许超卖赔付的场景。
结论:
为了绝对保证不超卖,主流电商都选择“下单即锁库存”。
所以,当你点击“提交订单”那一刻,数据库里的 stock 字段就已经 -1 了。哪怕你还没付钱,甚至关掉了支付窗口,这个库存依然被你的订单“锁住”了,别人买不了。

2️⃣ 为什么 TTL 过期后,如果不转死信队列,订单还是“待付款”?

这里有一个巨大的思维误区:计算机是“指令驱动”的,不是“时间驱动”的。
  • 你的直觉:时间到了(15 分钟),系统应该自动知道“哦,超时了”,然后把订单关掉。
  • 计算机的现实:数据库里的一条记录(订单),它没有生命,它不会自己看时间,也不会自己改变自己。
    • 除非有人(代码)拿着刀(UPDATE 语句)去砍它,否则它哪怕过了一万年,状态也永远是 待支付
流程拆解:
  1. 设置 TTL 的本质:
    • 你在 RabbitMQ 里设置 TTL=15m,只是给消息贴了个标签:“15 分钟后如果我还在,我就过期了”。
    • 注意:RabbitMQ 的默认行为是,消息过期 = 垃圾 = 删除。它不会自动触发任何业务逻辑,也不会自动去改数据库。
  2. 如果没有死信队列:
    • 15 分钟到 →→ 消息过期 →→ RabbitMQ 把消息删了。
    • 消费者(那段负责改数据库状态的代码):根本没收到消息,所以根本没运行。
    • 数据库:没人发 UPDATE order SET status='cancelled' 指令。
    • 结果:订单状态纹丝不动,依然是 待支付
  3. 如果有死信队列:
    • 15 分钟到 →→ 消息过期 →→ RabbitMQ 发现配置了死信 →→ 把消息转发给死信队列。
    • 消费者:监听到死信队列有新消息 →→ 代码运行了!
    • 消费者执行:UPDATE order SET status='cancelled' WHERE ...
    • 结果:数据库状态变成了 已取消,库存回滚。
结论:
TTL 只是一个计时器,它本身不会执行逻辑。必须配合死信队列把“过期的信号”传递给“干活的代码(消费者)”,代码才能去修改数据库。如果没有这个传递过程,时间到了也白搭,订单永远卡在那里。

3️⃣ 数据库状态为什么会变成“待支付”?是关掉付款界面变的吗?

不是的! 这是一个因果关系的误解。
真相是:
“待支付”状态是在你点击“提交订单”按钮的那一瞬间生成的,而不是在你关掉付款界面时生成的。
详细时间线:
  1. 浏览商品:你在看手机,此时数据库里没有你的订单。
  2. 点击“立即购买” -> “提交订单”(关键步骤!):
    • 前端发送请求给后端。
    • 后端代码执行:
      1. 创建订单记录:在数据库 orders 表插入一行新数据。
      2. 设置初始状态:这行数据的 status 字段直接被写入为 UNPAID (待支付)。
      3. 扣减库存:stock = stock - 1
      4. 发送延迟消息:告诉 MQ,“15 分钟后提醒我检查这个订单”。
    • 此时:数据库里已经有一条 待支付 的订单了。
  3. 弹出支付框:
    • 后端告诉前端:“订单创建成功了,去调起微信支付/支付宝吧。”
    • 前端弹出二维码或密码输入框。
  4. 你点击“❌”关掉支付框:
    • 发生了什么? 其实什么都没发生!
    • 你只是关闭了前端的一个弹窗(UI 层面)。
    • 你没有发送“取消订单”的请求给服务器。
    • 服务器根本不知道你关掉了窗口,它还以为你在输入密码呢。
    • 数据库状态:依然是第 2 步写入的 待支付,没有任何变化。
什么时候状态会变?
只有两种情况状态才会变:
  1. 你付钱了:支付平台回调通知服务器 →→ 服务器执行 UPDATE status = 'PAID'
  2. 超时取消了:15 分钟后,消费者收到死信消息 →→ 服务器执行 UPDATE status = 'CANCELLED'
  3. 你主动点了“取消订单”按钮:前端发送请求 →→ 服务器执行 UPDATE status = 'CANCELLED'
结论:
关掉支付界面只是一个前端动作,不会触发任何后端状态变更。订单之所以是“待支付”,是因为它在创建之初就被定义为了“待支付”,并且在等待后续的“支付成功”或“超时取消”指令来改变它。如果后续指令没来(比如消息丢了),它就永远孤独地保持“待支付”。

🌟 终极总结(串联三点)

  1. 为什么下单就减库存?
    为了防超卖。宁可错杀(锁住库存最后没买),不可放过(卖多了发不出货)。
  2. 为什么超时不自动取消?
    因为数据库不会自己变,MQ 过期默认是删除。必须靠死信队列把“过期信号”捡回来,交给代码去执行 UPDATE 语句。没这个环节,订单就卡死了。
  3. 为什么关掉支付框还是待支付?
    因为“待支付”是出生证明(创建订单时就有了),关掉支付框只是“无视它”,并没有“杀掉它”。只有明确的“支付成功”或“超时取消”指令才能改变它的命运。
这下是不是彻底通透了?这就是分布式系统中最终一致性和可靠性设计的精髓所在!
*Tesla* | 园豆:1516 (小虾三级) | 2026-02-25 17:33
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册