首页  >  问答  >  正文

redis - 防同一用户并发操作方案咨询

新客领取优惠券逻辑

同一用户并发领券的情况下 会有超领的情况 因为并发进来时查询数据库均没有领取记录

而又不能针对用户和优惠券做唯一索引 因为有些券允许用户领取多张。

于是决定采用redis计数来防同一用户并发超领 新客领券逻辑改成如下

上述逻辑没有问题 但纠结该如何设置计数key的过期时间

有没其他防并发领取方案呢? 可以不用纠结这种过期时间的设置呢。

滿天的星座滿天的星座2754 天前844

全部回复(5)我来回复

  • PHP中文网

    PHP中文网2017-04-27 09:04:40

    建议将防并发超领与判断是否新客 分开判断

    • 防并发超领可以使用以下(上锁), 每次操作完成记得删除key(释放锁)

    // 操作的原子性,如该key在有效时间30秒被设置过返回0,一般请求超时为30秒
    $redis->set($key, 1, array("NX", "EX"=>'30'));
    • 判断是否是新客(用户是否领取过,有效时间设置到活动结束时间),可以设置一个新的KEY作用户的领取状态,领取过直接返回给用户已经领取过优惠(追求并发性能时,不连接数据库查询)

    • 获取优惠卷(提前把优惠卷塞入队列,从队列里获取,不从数据库获取,此处的好处是,队列数据取完就没,不会出现超领取情况),写入redis队列

    • 后台进程消费队列,我用链表阻塞类型进行消费(将领取信息存放入库,入库前对用户是否领取过优惠卷进行查询判断)

    回复
    0
  • 黄舟

    黄舟2017-04-27 09:04:40

    具体的方案还是需要根据业务来实施

    1. 如果业务没有要求100%,每个人只能领一张券,并且你的Redis集群是高可用的.那么用Redis来挡就足够了,具体Key的失效时间,等活动下载再整体失效,因为这是一个不落库的方案,所以如果需要判断是否重复请求需要依赖Redis里面的数据(万一Redis挂了呢,虽然Redis有比价不完善的持久化功能,但是丢的数据量比较少,如果如果业务上允许,还是可以接受的),但是很多场景业务都需要要求落库

    2. 业务上要求,必须一人一券(可能券的面值很大),那么前面还是用题主的Redis方案,在判断完是第一次以后,直接插/更新数据库(这里就会有异步的方案,但是异步的方案也会出现,异步的方案也会出现与1类似的问题:丢数据),按照这个方案,可以保证每个人只能领一张券(由数据库来保证数据的一致性)。如题主所说,其实你不需要把Redis里面的东西失效掉,因为如果一个用户重复恶意请求抽券,直接走Redis来挡,不用去数据库查询(这里还会一个问题,该方案是先更新缓存再更新数据库,所以需要考虑缓存更新成功,但是数据库更新失败的异常情况,如果数据库异常需要清理该Key对应的缓存)

    3. 如果实在扛不住(硬件跟不上),可以用队列来解决,用队列来削峰

    4. @大呜 的方案也是可以行的,但是需要考虑某一时刻Redis这种异常情况

    回复
    0
  • 滿天的星座

    滿天的星座2017-04-27 09:04:40

    @大呜 提到的功能其实就是类似于秒杀,我以前写过一篇文章。你可以看看:
    电商类产品的必备功能:秒杀与竞拍

    回复
    0
  • 滿天的星座

    滿天的星座2017-04-27 09:04:40

    写队列不行吗

    回复
    0
  • 淡淡烟草味

    淡淡烟草味2017-04-27 09:04:40

    这种事情你敢用nosql数据库我也是醉了。

    建议先学学数据库的理论,重点看看事务概念、事务的几种隔离级别、行锁表锁,然后用传统关系型数据库来做这个事情。

    如果一台数据库服务器的性能不够,那就设计成水平分表架构,不同的服务器,每台服务器承担一部分用户数据。水平分表的理论来源是本科级别的数据结构的hash章节,你可以补习一下。

    回复
    0
  • 取消回复