有个关于并发的问题想请教下

2021-01-05 18:04:12 +08:00
 jtping

最近在做一个电商项目,现在到了扣库存这一块,扣库存时有什么好的上锁方案吗 (要有一定的并发能力)

具体是这样的:

最开始用 redisson 分布式锁把整个生成订单事务都锁了(扣库存操作在里面),但这样效率不高。

现在改用悲观锁(用的 MySQL ),可感觉悲观锁的效率还是不够高,我也去网上找过,都不太建议库用悲观锁。

想过把库存信息都放到 reids 中,把所有库存信息都放到 reids 中可行吗?有没有什么弊端

之前这方面接触的也不多,有没有什么好的解决方案,望大神赐教

2165 次点击
所在节点    程序员
25 条回复
jtping
2021-01-05 20:10:00 +08:00
@securityCoding 哈哈刚开始不懂事 就是这么锁的 后来改成行锁了
securityCoding
2021-01-05 20:18:46 +08:00
@jtping 相关操作都先通过库存组件 ,DB 保持最终一致性,缓存策略看看这篇文章吧
https://coolshell.cn/articles/17416.html
jtping
2021-01-05 20:25:55 +08:00
@securityCoding 感谢!
crclz
2021-01-05 21:21:37 +08:00
@opengps #8 楼说的非常正确,要考虑的情况很多,逻辑要求应当非常严谨。

现在市面上的文章很多都没考虑到数据的一致性。(例如 redis 扣减库存,然后 DB 再慢慢处理订单)


经过分析,不难发现,redis+DB 有以下两个主要 ACID 方面的问题:

1. "ACID.Consistency"(一致性):因为 redis 和 DB 是两个数据储存,所以涉及到分布式事务。
分布式事务一般有如下两种选择:A. 采用某种协议,例如 Paxos,保证强一致性。B. 采用消息队列+补偿来保证最终一致性。

2. "ACID.Duration"(持久性):redis 断电后如何恢复?
一般可以采用的是:A. 副本集 B. 平行系统( Parallel Model - Martin Fowler )

对于“一致性”问题,A 方案(分布式事务协议)是不现实的,因为 redis 本身的事务支持就不完备。如果采用 B 方案,用消息队列,也是无法实现的,因为 outbox pattern (自己去查英文资料)依赖 ACID 的单个数据库的事务,而 redis 本身的事务支持就不完备。

现在,我们发现,万恶之源在于 —— redis 对事务的支持不完备。

-------

但是,我们可以转换一下思路,把 redis 的角色从更偏向 DB 的角色转换成更偏向 cache 的角色。
这时候就应当引入 [软状态] :状态定期过期并刷新。

简单来说,就是定期把剩余库存定期更新到 redis 。

接下来详细说一下:

假设我们的步骤如下:
1. 连接 redis,库存-1,如果成功(可以使用 lua 脚本来保证原子性),那么就进行下一步;如果失败,就提示已售罄。
2. 连接数据库,创建订单(或者为了削峰,写消息队列,事后慢慢在 DB 创建订单)

但是,这两步不是原子性的,会造成 redis 和 DB 的状态不一致:
如果 redis 扣减库存成功了,但是连接数据库失败了,那么就会存在少卖的情况。但是不会超卖。幸运的是,少卖比超卖好解决。

这时候,只需要在消息队列里面的订单处理完成后,将 DB 里面真实的剩余库存同步到 redis 即可。
jtping
2021-01-06 08:52:42 +08:00
@crclz 多谢赐教

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/741961

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX