关于使用redis防高并发超中奖品设置过期时间的一个疑问

 mobiledu2502869467 发布于 2022-10-25 09:26

业务

大转盘抽奖活动 奖品分实物和红包 限制用户只能中一个实物

使用redis防同一用户并发超领实物 即中了多个实物
获得奖池

AwardPool chooseAwardFromPool(){ //得到奖池
    // 查询所有有效奖品
    // 若用户之前已中了实物 排除实物奖品
    if(hasWinedRealObject && award.type==实物)
        continue;
    //...
}
award = chooseAwardFromPool(pool); //从奖池中随机选择一个奖品

若用户还未抽中实物, 且同一用户并发进入, 存在随机选择的奖品都为实物的可能, 如同一用户10个并发请求进来, 其中3个请求碰巧随机选择的奖品均为实物, 于是该用户就能中3个实物, 于是需要引入redis来防并发超中实物. 如下所示

//选中奖品后处理
if(award.type == 实物){ // 若奖品为实物
    key = "user_"+userId+"_实物_count";
    count = redis.incr(key);
    if(count == 1){
        redis.expire(key, 10*60); //设置过期时间10分钟
    }
    if(count > 1){ //同一用户中了多个实物
        award = 未中奖; //此时默认替换为未中奖奖品
    }
}

过期时间我始终不知该如何评估, 设置多长时间合适, 因为基本上是针对恶意用户并发请求才引入redis的, 正常用户的正常页面操作无需做任何处理, 因为若前一次中了实物,后面再来抽奖的话, 一开始取得奖池时就会排除掉实物奖品, 故后面抽奖奖池中压根就没有实物奖品了, 也就不会中实物了.
为什么设置10分钟呢? 因为我觉得两个并发请求--且是均中了实物的两个请求--不可能执行redis.incr(key)时相隔了10分钟, 如下所示

#请求1  用户尚未中实物 从奖池中随机返回了一个实物奖品
award = chooseAwardFromPool(pool); //从奖池中随机选择一个奖品

#请求n  用户尚未中实物 也从奖池中随机返回了一个实物奖品
award = chooseAwardFromPool(pool); //从奖池中随机选择一个奖品

#请求1 执行redis操作
count = redis.incr(key);

#请求n 执行redis操作
count = redis.incr(key);

我觉得10分钟能够保证请求n执行redis操作时, key不会过期, 故能够防超中实物.

但又不是很笃定, 怎觉得存在请求2执行时key会过期的情况, 但又想不出什么情况下会有这样的情况.
请求数并发量特别大的情况下会存在这种可能吗? 如

ab -n 1000000 -c 1000 -T "application/x-www-form-urlencoded" -p post_draw http://localhost:8080/draw

如请求1过来的时候随机选中了一个实物, 等到请求n过来的时候, 请求1还没有提交到数据库中, 于是请求n有可能随机返回一个实物奖品, 等到请求n执行redis.incr(key)时, 已经过了10分钟了, 于是请求n仍能中实物.于是同一用户中了两个实物.

1 个回答
  • 1.按照你的设计用redis来处理,你为什么不把时间直接设为永久呢?等请求A事物提交后再清除,接着就全走你正常的逻辑判断
    2.不用redis,借助数据库实现多并发控制,首先你肯定有个奖池表,里面会有字段表示获奖人,每次用户中奖时会去更新奖池表设置获奖人,然后你在进行sql更新的时候加上实物奖数量控制条件

    2022-10-26 23:02 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有