最近在做一个电商项目,现在到了扣库存这一块,扣库存时有什么好的上锁方案吗 (要有一定的并发能力)
具体是这样的:
最开始用 redisson 分布式锁把整个生成订单事务都锁了(扣库存操作在里面),但这样效率不高。
现在改用悲观锁(用的 MySQL ),可感觉悲观锁的效率还是不够高,我也去网上找过,都不太建议库用悲观锁。
想过把库存信息都放到 reids 中,把所有库存信息都放到 reids 中可行吗?有没有什么弊端
之前这方面接触的也不多,有没有什么好的解决方案,望大神赐教
1
jtping OP 自定
|
2
xiaomu8 2021-01-05 18:17:19 +08:00
开发日活百万
上线日活一百 |
4
YouLMAO 2021-01-05 19:39:26 +08:00 via Android
一般都是这样的,为什么效率不高? 有没有分析过生成订单最慢的 top 5 操作
|
6
simonlu9 2021-01-05 19:48:57 +08:00
库存采用分段锁,可以提高效率
|
8
opengps 2021-01-05 19:52:45 +08:00 2
库存放在 redis 很常见,弊端就是对逻辑要求比较严谨,需要综合考虑各种极端情况,比如:
断电怎么办 锁是否彻底能够做到库存不超额 数据库变更时候缓存刷新是否有残留变更 等等 |
9
YouLMAO 2021-01-05 20:00:05 +08:00 via Android 1
我觉得网民和我讨论场景不同,他们是考虑秒杀? 我是考虑有 5 千万商品在售,每件商品 50 库存,一分钟有 1 万人下单但下的是不同商品呀
|
11
securityCoding 2021-01-05 20:02:34 +08:00
独立库存组件出来 , 配合 redis 用 write through(这玩意不知道咋翻译)方式管理库存 ,后面挂个队列异步通知 db 扣减库存(最终一致性)
|
12
YouLMAO 2021-01-05 20:03:55 +08:00 via Android
不是秒杀场景用 Redis,可以准备跑路
|
13
securityCoding 2021-01-05 20:04:31 +08:00
@YouLMAO 5000w sku 你真敢想...
|
17
jtping OP @securityCoding 方便给个关键词吗 我去网上找找
|
19
securityCoding 2021-01-05 20:08:26 +08:00 1
@jtping 小伙子 ,你是不是分布式锁把整个下单接口都锁住了? 锁下单商品那个 sku 就行了
|
21
jtping OP @securityCoding 哈哈刚开始不懂事 就是这么锁的 后来改成行锁了
|
22
securityCoding 2021-01-05 20:18:46 +08:00
|
23
jtping OP @securityCoding 感谢!
|
24
crclz 2021-01-05 21:21:37 +08:00 4
@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 即可。 |