V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mamasan
V2EX  ›  Redis

使用 Redis 计数的问题

  •  
  •   mamasan · 2019-09-19 11:11:08 +08:00 · 15020 次点击
    这是一个创建于 1869 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如有个业务需求售卖某种商品, 为了防止超发, 使用 redis 原子计数.

    但是 incr 是, 如果 key 未命中会从 0 从新开始.

    那是不是这种情况就不能用 redis 去做计数了?

    21 条回复    2019-10-02 21:23:28 +08:00
    optional
        1
    optional  
       2019-09-19 11:13:23 +08:00
    我的方案,写一个 incrNx 的 lua 脚本
    tt67wq
        2
    tt67wq  
       2019-09-19 11:21:02 +08:00
    自减呢?
    zpfhbyx
        3
    zpfhbyx  
       2019-09-19 13:18:32 +08:00
    商品如果数量不是很大的话,搞个 redis 的队列,卖一个取一个啊.
    zjsxwc
        4
    zjsxwc  
       2019-09-19 13:29:12 +08:00
    最简单是跑个进程检查新支付的订单是否超卖,发现超卖就退款取消订单

    fluorinedog
        5
    fluorinedog  
       2019-09-19 13:45:32 +08:00
    用 decr 啊,先把商品数量仍进 redis
    mamasan
        6
    mamasan  
    OP
       2019-09-19 14:30:04 +08:00
    decr 一样也会未命中啊, 那不是少发了?
    x537196
        7
    x537196  
       2019-09-19 14:45:25 +08:00
    一般使用 3L 的方案
    Thinklong
        8
    Thinklong  
       2019-09-19 15:44:46 +08:00
    你们都是几年的工程师?还在用队列或者自减?用分布式锁啊 setnx
    phantomzz
        9
    phantomzz  
       2019-09-19 16:31:31 +08:00 via Android
    为什么会未命中?感觉 incr 和 decr 没问题啊,我项目里用这个限制桶大小,一年多了没出现过这个问题。
    phantomzz
        10
    phantomzz  
       2019-09-19 16:33:40 +08:00 via Android
    @Thinklong setnx 如何实现可重入?退一步讲有简单的方案没必要用分布式锁,本身就是高并发场景,增加 io 开销确定是好方案吗?
    yc8332
        11
    yc8332  
       2019-09-19 16:58:34 +08:00
    哪里会有命中问题啊?你是不是对命中的理解有偏差。。。未命中是说缓存中没有需要去数据库取,你这种计数的不存在这种问题。
    sujin190
        12
    sujin190  
       2019-09-19 17:44:10 +08:00
    @phantomzz #10 先加判断超过了,然后下单失败再减下来?
    如果是做成锁的话,本来就不可重入吧,什么时候锁也可以重入了

    楼主的问题是是不是商品一直在卖,担心中间有空闲再开卖有可能 redis key 不存在了,然后可能会超?其实感觉如果你用来做库存限制,那么 redis 就不应该是缓存,而应该是持久化数据库,所以 key 不应该超时,持久化要开,每次商品修改也要同步更新 redis 值才行吧,否则就做成分布式锁吧,库存的更新检查还是以数据库为主
    phantomzz
        13
    phantomzz  
       2019-09-19 19:19:36 +08:00
    @sujin190

    考虑再三还是决定回你一下,如果不知道可重入锁,并发编程的书再去看一看。入门知识,你应该毕业不久。

    在高并发场景下,不建议直接读写数据库,建议 缓存+队列 实现 异步+削峰,redis 控制超卖(不谈风控),然后丢进队列,中 /后台慢慢消费,异步处理订单后续流程。使用分布式锁+DB 去同步处理订单,增加不必要的 IO 开销。这些例子网上都烂大街了,可以到处去找来看一看
    earther01
        14
    earther01  
       2019-09-19 19:24:45 +08:00 via iPhone
    我想知道针对楼主防止超卖的问题,把库存提前加进 redis 里,每次卖出一个就原子减一,减到小于 0 就报无库存,有啥问题?
    phantomzz
        15
    phantomzz  
       2019-09-19 19:33:14 +08:00   ❤️ 1
    @earther01 楼主应该对 redis 的 hit 和 miss 有一点点误解,按照他的提问,我猜测他的理解应该是:访问一个 redis 中实际存在的 key,是有几率 miss 的…
    sujin190
        16
    sujin190  
       2019-09-19 21:39:15 +08:00
    @phantomzz #13 如果你说的是操作系统那个可重入锁的话,认真说这真是个很不严谨且偷懒的设计,web 这种场景中更不应该出现,难道你只加锁不解锁的么,需要啥重入

    而且你这给出建议不考虑实际场景啊,大多数情况下,加个锁增加的 io 多还是队列异步中后台一大串 io 多,还不说维护复杂性在那呢,大多数场景下,加锁加直接该数据库库存是最容易实现最容易维护最容易稳定的方案了,能在这种情况下用满数据库 IO 的都已经是很高销售额了
    dusu
        17
    dusu  
       2019-09-20 00:54:00 +08:00 via iPhone
    inc 和 dec 都需要额外处理高并发下一次购买多件的问题
    zuicaidenage
        18
    zuicaidenage  
       2019-09-20 09:54:09 +08:00
    lua 了解一下
    capljf
        19
    capljf  
       2019-09-20 10:35:18 +08:00
    如果库存有变更 新增库存或者分销走一部分库存 ,那 inc 和 dec 都不好使了吧。看业务场景
    Aresxue
        20
    Aresxue  
       2019-09-20 10:46:28 +08:00
    @phantomzz 你这种方式可以说是很快的一种方案了,业务要求没那么严格的话(比如 5000 件不能超卖但是可以少一小部分)是一个很不错的方案,但如果要求强一致性的业务(5000 件都要卖完不多不少或者少的数量有严格限制要低于多少),你异步处理的回滚可能不够及时导致最终少卖了一部分,这时候其实是有必要上分布式锁的,为了一致性性能是要牺牲一些的。所以说来说去还是那句废话,看业务。。。
    mamasan
        21
    mamasan  
    OP
       2019-10-02 21:23:28 +08:00
    谢谢各位回复
    其实我要问的问题, 没有那么复杂。
    我其实只是想确认下,有些简单的业务需要控制库存的时候,使用 redis 计数是否是可靠的。
    比如,Redis 我使用默认的 rdb 持久化,准备 10 个库存,会不会存在计数的过程中,库存 count (假设这个 key 没有设置 ttl )这个 key 丢失了, 然后造成从 0 开始从新计数?
    因为很久以前使用 memcache 计数的时候, 出现过这种情况的。
    不过后来想一想,是不是当 redis 配置了持久化之后,当内存满了, 应该不能写入了,已存在的 key 应该是怎么也不会丢的吧?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1449 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 23:54 · PVG 07:54 · LAX 16:54 · JFK 19:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.