前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >系统重试,导致库存扣多啦,怎么办(两行代码破解)?(第41讲)

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

作者头像
架构师之路
发布2025-03-06 23:10:39
发布2025-03-06 23:10:39
510
举报
文章被收录于专栏:架构师之路架构师之路

《架构师之路:架构设计中的100个知识点》 41.重复扣减

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

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

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

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

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

图片
图片

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

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

select num from stock where sid=$sid

2. 库存扣减接口,微服务一般执行:

update stock set num=num-$reduce where sid=$sid

3. 库存设置接口,微服务一般执行:

update stock set num=$num_new where sid=$sid

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

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

图片
图片

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

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

图片
图片

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

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

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

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

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

图片
图片

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

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

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

图片
图片

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

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

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

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

图片
图片

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

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

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

图片
图片

如上图所示:

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

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

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

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

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

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

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

实际执行的时候:

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

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

画外音:有条件的成功。

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

update stock set num=$y where sid=$sid

升级为:

update stock set num=$num_new where sid=$sid

and num=$num_old

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

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

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

总结

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

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

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

知其然,知其所以然。

思路比结论更重要。

==全文完==

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构师之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档