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

大并发下的抽奖有什么好的实现思路吗?

  •  1
     
  •   woshipanghu · 2020-02-12 22:06:58 +08:00 · 4621 次点击
    这是一个创建于 1771 天前的主题,其中的信息可能已经有所发展或是发生改变。

    并发的时候 怎么保证被抽的奖品确实还有数量

    数据库 mysql 和 redis 都可以用

    v 友们有什么好思路吗

    25 条回复    2020-02-13 23:24:52 +08:00
    woshipanghu
        1
    woshipanghu  
    OP
       2020-02-12 22:23:09 +08:00
    我自己想的一个逻辑
    奖品数量如果只剩下 1 个的时候,用户抽到这个奖品的时候需要去 redis 拿一个令牌,拿到令牌的用户获得这个奖品。
    其他用户,随机获取剩余奖品中的一样。
    jindeq
        2
    jindeq  
       2020-02-12 22:27:10 +08:00 via Android
    @woshipanghu 并发量足够大的情况下,redis 可能崩的情况下,还需要其他的途径确保唯一性
    woshipanghu
        3
    woshipanghu  
    OP
       2020-02-12 22:30:20 +08:00
    @jindeq 只要服务器正常 redis 我倒不担心崩掉,并发只要抢最后一个奖品的时候会用 redis redis 的并发不会太高
    lsylsy2
        4
    lsylsy2  
       2020-02-12 22:35:42 +08:00
    比如奖品有 100 件,中奖率 1%
    那么就建一个队列,里面有 10000 个 object,object 的内容是中奖或不中奖,提前生成内容随机打乱
    每个“抽奖”的行为就是从队列取一个 object,取不到=谢谢惠顾
    高并发的话,可以把 1 个 10000 长度的队列切成 100 个 100 长度的队列,互相独立,每个 client 随机选一个队列去取。
    sujin190
        5
    sujin190  
       2020-02-12 22:43:03 +08:00
    @lsylsy2 #4 抽奖不是秒杀,假设活动持续三天那么应该是尽可能的匹配人流分布在整个时间段内平均的抽中,否则万一一开始就全抽完了,后面就没法玩了,所以用队列看起来不是很科学吧
    optional
        6
    optional  
       2020-02-12 22:47:33 +08:00
    @sujin190 用队列是可以的,把奖品按照时间分片,分成 n 个几个队列就好(比如队列名加入小时参数)。
    sujin190
        7
    sujin190  
       2020-02-12 22:47:38 +08:00
    我感觉不需要那么复杂,加锁就是了吧,反正概率算法确定是否中奖这个不需要加锁,所以完全可以先抽中从奖池真正取奖品的时候加锁保证唯一性就好了,抽奖人可能很多,中奖人不可能也很多吧

    如果中奖人也很多,那说明你这奖品也不值钱,那就更无所谓了,就算有并发问题,多发个几十几百的这算个毛事,根本无需考虑吧
    sujin190
        8
    sujin190  
       2020-02-12 22:50:44 +08:00
    @optional #6 队列的问题是抽奖人数无法准确预估,如果预估比实际少,一开始很快抽完了会影响活动效果,预估比实际多,受队列限制无法自适应提高中奖概率,也会影响活动效果
    abcbuzhiming
        9
    abcbuzhiming  
       2020-02-12 23:21:59 +08:00
    我见过一个简单的思路,抽奖的请求全部排队,只允许和当前奖品剩余数量相等的请求数进入队列,超出的请求立刻返回“未中奖”。然后进入队列的请求再进行抽奖逻辑,绝不会超出剩余奖品,而且用户体验也还可以,当然对上专业黑产羊毛党会很头疼
    lxml
        10
    lxml  
       2020-02-12 23:24:33 +08:00
    提前按照机器分好,放在内存里,然后对账,准确性和并发你只能选一样
    FaceBug
        11
    FaceBug  
       2020-02-12 23:26:33 +08:00
    @sujin190 抽奖时段如果有三天这种跨度,一般都会分场次了。不需要绝对的考虑公平的问题,因为没法预估第二天流量是否还会保持第一天的热度,也没法确认第一天的热度到底有多高是吧。如果只有一个场次,持续三年,人为的限制了第一天的中奖率,结果第二天第三天没人来了,要么奖品送不完,要么认为干预,导致来的几乎都有奖。
    FaceBug
        12
    FaceBug  
       2020-02-12 23:28:13 +08:00
    本质上是和秒杀一个思路的。先判断是否中奖,中奖了再从奖品队列弹出一个奖品。如果奖品不足,也认为是没有中奖。
    fewok
        13
    fewok  
       2020-02-12 23:33:31 +08:00
    预估下多少并发,每秒 100W 次? 1 亿次?
    wAtcher789
        14
    wAtcher789  
       2020-02-12 23:35:07 +08:00 via Android
    @abcbuzhiming 小米 :我怀疑你看了我们代码
    aliipay
        15
    aliipay  
       2020-02-13 00:37:35 +08:00
    @wAtcher789 小米: 前端 js hash 一下 uid,直接过滤大部分用户请求 (当然后端也会同样 hash 处理), 你不中奖有可能是你 uid 不好 [:doge]
    horkooo
        16
    horkooo  
       2020-02-13 00:54:04 +08:00 via Android
    题目描述比较简单,可以理解成秒杀的场景。对应秒杀场景的解决方案。同时也要兼顾活动的可持续性。每天放出的奖是剩余奖品除以剩余天数。抽奖,奖品,中奖数据全部放 redis,最后合并写库就行了。
    EminemW
        17
    EminemW  
       2020-02-13 01:00:01 +08:00
    @aliipay 所以有的人很容易中奖,身边一个长辈,有一段很迷小米一元购,抽中很多
    18ac0877
        18
    18ac0877  
       2020-02-13 01:02:30 +08:00
    将抽奖和公布结果分离,xxx-xxx 时间抽奖,xxx-xxx 时间后台根据算法抽奖,xxx 时间公布结果。
    18ac0877
        19
    18ac0877  
       2020-02-13 01:09:34 +08:00
    另一个办法:后台先预定哪些用户能中奖,提前算好,如果该用户抽奖了直接返回中奖。
    murmur
        20
    murmur  
       2020-02-13 07:36:38 +08:00
    return 未中奖
    lqw3030
        21
    lqw3030  
       2020-02-13 08:58:56 +08:00
    削峰,然后进队列
    hand515
        22
    hand515  
       2020-02-13 10:14:15 +08:00
    先通过随机数,把一部分流量的直接返回没抽中,这个量可以根据实际流量配置,如 50%。
    xiaoyouqiang
        23
    xiaoyouqiang  
       2020-02-13 10:55:30 +08:00
    这个问题说的比较抽象,大并发解决方案本身就是一个大的领域。什么队列,缓存,复杂均衡都是手段,你要结合自己的情况,搭建一套大并发体系。
    LoremIpSum
        24
    LoremIpSum  
       2020-02-13 22:29:53 +08:00
    1.请求进来
    2.根据预先配置好的中奖概率来判断这个请求能不能中奖
    3.如果可以中奖,锁定库存( redis 分布式锁),返回结果,反之直接返回未中奖
    hankai17
        25
    hankai17  
       2020-02-13 23:24:52 +08:00
    是微 xin 的发 red bag 吗?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3001 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 14:26 · PVG 22:26 · LAX 06:26 · JFK 09:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.