有个关于并发的问题想请教下 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
jtping
V2EX    程序员

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

  •  1
     
  •   jtping 2021-01-05 18:04:12 +08:00 2709 次点击
    这是一个创建于 1772 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    具体是这样的:

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

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

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

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

    25 条回复    2021-01-06 08:52:42 +08:00
    jtping
        1
    jtping  
    OP
       2021-01-05 18:11:06 +08:00
    自定
    xiaomu8
        2
    xiaomu8  
       2021-01-05 18:17:19 +08:00
    开发日活百万
    上线日活一百
    jtping
        3
    jtping  
    OP
       2021-01-05 18:19:43 +08:00
    @xiaomu8 哈哈哈 还是要稍微做点准备的 万一火了呢
    YouLMAO
        4
    YouLMAO  
       2021-01-05 19:39:26 +08:00 via Android
    一般都是这样的,为什么效率不高? 有没有分析过生成订单最慢的 top 5 操作
    jtping
        5
    jtping  
    OP
       2021-01-05 19:48:50 +08:00
    @YouLMAO 这个还真不了解 望指点
    simonlu9
        6
    simonlu9  
       2021-01-05 19:48:57 +08:00
    库存采用分段锁,可以提高效率
    jtping
        7
    jtping  
    OP
       2021-01-05 19:51:23 +08:00
    @simonlu9 应该还不到用分段锁的地步
    opengps
        8
    opengps  
       2021-01-05 19:52:45 +08:00   2
    库存放在 redis 很常见,弊端就是对逻辑要求比较严谨,需要综合考虑各种极端情况,比如:
    断电怎么办
    锁是否彻底能够做到库存不超额
    数据库变更时候缓存刷新是否有残留变更
    等等
    YouLMAO
        9
    YouLMAO  
       2021-01-05 20:00:05 +08:00 via Android   1
    我觉得网民和我讨论场景不同,他们是考虑秒杀? 我是考虑有 5 千万商品在售,每件商品 50 库存,一分钟有 1 万人下单但下的是不同商品呀
    jtping
        10
    jtping  
    OP
       2021-01-05 20:00:51 +08:00
    @opengps 学习了 感谢
    securityCoding
        11
    securityCoding  
       2021-01-05 20:02:34 +08:00
    独立库存组件出来 , 配合 redis 用 write through(这玩意不知道咋翻译)方式管理库存 ,后面挂个队列异步通知 db 扣减库存(最终一致性)
    YouLMAO
        12
    YouLMAO  
       2021-01-05 20:03:55 +08:00 via Android
    不是秒杀场景用 Redis,可以准备跑路
    securityCoding
        13
    securityCoding  
       2021-01-05 20:04:31 +08:00
    @YouLMAO 5000w sku 你真敢想...
    jtping
        14
    jtping  
    OP
       2021-01-05 20:04:49 +08:00
    @YouLMAO 我就是这个意思!用悲观锁 万一有其他请求进来就要等了 用户体验不太好
    jtping
        15
    jtping  
    OP
       2021-01-05 20:06:03 +08:00
    @YouLMAO 没呢 项目还没上线
    YouLMAO
        16
    YouLMAO  
       2021-01-05 20:06:28 +08:00 via Android
    @jtping 那你是秒杀还是非秒杀???下单的是不同商品,不会互相锁库存喔为什么要等
    jtping
        17
    jtping  
    OP
       2021-01-05 20:07:02 +08:00
    @securityCoding 方便给个关键词吗 我去网上找找
    YouLMAO
        18
    YouLMAO  
       2021-01-05 20:08:17 +08:00 via Android
    @jtping 分段锁也是秒杀场景的,直接百度
    securityCoding
        19
    securityCoding  
       2021-01-05 20:08:26 +08:00   1
    @jtping 小伙子 ,你是不是分布式锁把整个下单接口都锁住了? 锁下单商品那个 sku 就行了
    jtping
        20
    jtping  
    OP
       2021-01-05 20:08:55 +08:00
    @YouLMAO 就是普通的购买 比如 1 秒内对同一个商品下单 10 次 这个就需要等了(这种情况应该会出现吧)
    jtping
        21
    jtping  
    OP
       2021-01-05 20:10:00 +08:00
    @securityCoding 哈哈刚开始不懂事 就是这么锁的 后来改成行锁了
    securityCoding
        22
    securityCoding  
       2021-01-05 20:18:46 +08:00
    @jtping 相关操作都先通过库存组件 ,DB 保持最终一致性,缓存策略看看这篇文章吧
    https://coolshell.cn/articles/17416.html
    jtping
        23
    jtping  
    OP
       2021-01-05 20:25:55 +08:00
    @securityCoding 感谢!
    crclz
        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 即可。
    jtping
        25
    jtping  
    OP
       2021-01-06 08:52:42 +08:00
    @crclz 多谢赐教
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1022 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 18:48 PVG 02:48 LAX 10:48 JFK 13:48
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86