系统重试,导致库存扣多啦,怎么办(两行代码破解)?

大家有没有遇到过,库存异常的情况?接下来,就今天和大家聊一聊库存扣减里的方案设计。
首页 新闻资讯 行业资讯 系统重试,导致库存扣多啦,怎么办(两行代码破解)?

大家有没有遇到过,库存异常的情况:

  • 系统重试,导致库存扣了多次;

  • 系统并发,导致库存设置错误;

今天和大家聊一聊库存扣减里的方案设计。

f94b48053366ef8a04f1663d988d665febd527.jpg


库存微服务一般提供什么接口?

a7dd77a231d1b86c2de763faec1cc151300436.webp

提供库存的查询、扣减、设置等RPC接口:

  • 库存查询接口,微服务一般执行:

select num from stock where sid=$sid
  • 库存扣减接口,微服务一般执行:

update stocksetnum=num-$reduce where sid=$sid
  • 库存设置接口,微服务一般执行:

update stocksetnum=$num_new where sid=$sid


库存操作,一般是什么业务场景?

用户下单前,一般会对库存进行查询,有足够的存量才允许扣减:

56fe17343b71a3e66048489c39856b6283096d.webp

如上图所示,通过查询接口,得到库存是5。


用户下单时,接着会对库存进行扣减:

d1d90fc625b78e03a0b81799d42226b1559d95.webp

如上图所示,购买3单位的商品,通过扣减接口,最终得到库存是2。


简言之,一般是“先查后减”。


库存“先查后减”会遇到什么问题?

系统往往有重试机制,这个重试机制可能实现在系统底层,例如:服务连接池重试,数据库连接池重试,业务代码不可控。


如果通过扣减接口来修改库存,在重试时会导致重复扣减:

1590d981222eb04b9fa449197a2599cff78652.webp

如上图所示,try和retry,导致一次扣减执行两次,最终得到一个错误库存。


如何解决“重试导致库存异常”的问题?

这里的根本原因:“reduce”操作是一个非幂等的操作,不能够重复执行,可以升级为“set”操作:

5365a3d8181d974cc7c26358b53658a1a16bab.webp

如上图所示,同样是购买3单位的商品,通过set操作,即使有重试机制,也不会得到错误的库存,“set”操作是一个幂等操作。


因此,应该“先查后设”。


库存“先查后设”会遇到什么问题?

并发量很大时,还是可能导致库存异常:

83b005e39472a278c57494de9efe4ae6ca2d3c.webp

如上图所示,两个并发的操作,查询库存,都得到了库存是5。


接下来多个用户发生了并发的购买动作:

画外音:秒杀类业务特别容易出现。

e66981f05c5762dc0d4461b05fa87c87a9217d.webp

如上图所示:

  • 用户1购买了3个库存,库存要设置为2;

  • 用户2购买了2个库存,库存要设置为3;

  • 这两个设置库存的接口并发执行,库存会先变成2,再变成3,导致数据不一致(实际卖出了5件商品,但库存只扣减了2,最后一次设置库存会覆盖和掩盖前一次并发操作);


如何解决“并发导致库存异常”的问题?

这里的根本原因:设置操作发生的时候,没有检查库存与查询出来的库存有没有变化,理论上:

  • 库存为5时,用户1的库存设置才能成功;

  • 库存为5时,用户2的库存设置才能成功;


实际执行的时候:

  • 库存为5,用户1的set stock 2确实应该成功;

  • 库存变为2了,用户2的set stock 3应该失败掉;

画外音:有条件的成功。


接口实现优化升级,将库存设置接口执行的:

update stocksetnum=$y where sid=$sid

升级为:

update stocksetnum=$num_new where sid=$sid 
and num=$num_old

画外音:加了一个初始条件比对。


简言之,“先查后设,有条件的设”。


这正是大家常说的“Compare And Set”(CAS),是一种常见的降低读写锁冲突,保证数据一致性的方法。


总结

  • “先查后减”,在重试,并发的场景下,容易出现异常;

  • “先查后设”,幂等性优化,能够解决重试的问题;

  • “先查后设,有条件的射”,CAS优化,能够解决并发的问题;


知其然,知其所以然。

思路比结论更重要。


28    2025-03-06 08:00:00    库存 微服务 架构