PHP + Redis 实现抽奖算法(ThinkPHP5)

devtools/2025/1/7 22:03:37/

在使用 ThinkPHP5 和 Redis 实现抽奖算法时,我们可以结合 Redis 的高性能数据结构来管理奖品的库存、用户的抽奖机会,并确保并发情况下的公平性和一致性。下面是一个实现抽奖算法的示例,涵盖了以下步骤:

  1. 设置奖品池:将奖品和奖品数量存储在 Redis 中。
  2. 用户抽奖:根据抽奖概率,从奖品池中抽取奖品。
  3. 中奖处理:扣减奖品库存并记录中奖信息。
  4. 避免重复中奖:确保用户在抽奖期间不会重复抽中相同的奖品。

1. 初始化奖品池及其中奖概率

首先,在初始化奖品池时,为每个奖品设置库存和中奖概率。这里的中奖概率总和可以小于或大于100%。

在 Redis 中创建一个哈希表来存储奖品及其库存。可以在 ThinkPHP 的控制器中初始化奖品池。

php">use think\facade\Cache;class LotteryController {// 初始化奖品池public function initPrizePool() {$prizes = ['prize1' => ['quantity' => 10, 'probability' => 50],  // 奖品1,库存10个,中奖概率50%'prize2' => ['quantity' => 5,  'probability' => 30],  // 奖品2,库存5个,中奖概率30%'prize3' => ['quantity' => 1,  'probability' => 15],  // 奖品3,库存1个,中奖概率15%'prize4' => ['quantity' => 50, 'probability' => 5]    // 奖品4,库存50个,中奖概率5%];foreach ($prizes as $prize => $data) {Cache::store('redis')->hSet('prize_pool', $prize, $data['quantity']);Cache::store('redis')->hSet('prize_probabilities', $prize, $data['probability']);}return json(['status' => 'Prize pool initialized']);}
}

2. 高并发下用户抽奖逻辑

编写抽奖逻辑,随机抽取奖品,加入 Redis 锁的获取和释放逻辑:

php">use think\facade\Cache;class LotteryController {// 用户抽奖public function draw() {$userId = session('user_id'); // 获取当前用户ID$lockKey = "lock:draw"; // 锁的键$lockTimeout = 5; // 锁定时间(秒)// 尝试获取锁if (Cache::store('redis')->set($lockKey, $userId, ['nx', 'ex' => $lockTimeout])) {try {// 检查用户是否已经中奖,防止重复抽奖if (Cache::store('redis')->hExists('user_prizes', $userId)) {return json(['status' => 'fail', 'message' => 'You have already won a prize!']);}// 获取奖品池$prizePool = Cache::store('redis')->hGetAll('prize_pool');if (empty($prizePool)) {return json(['status' => 'fail', 'message' => 'No prizes available']);}// 获取奖品的概率列表$prizeProbabilities = Cache::store('redis')->hGetAll('prize_probabilities');// 计算总概率$totalProbability = array_sum($prizeProbabilities);// 生成一个随机数$rand = mt_rand(1, $totalProbability);// 根据随机数选择奖品$cumulativeProbability = 0;$selectedPrize = null;foreach ($prizeProbabilities as $prize => $probability) {$cumulativeProbability += $probability;if ($rand <= $cumulativeProbability) {$selectedPrize = $prize;break;}}// 确认有选中奖品if (!$selectedPrize) {return json(['status' => 'fail', 'message' => 'Sorry, better luck next time']);}// 检查奖品库存并更新$remaining = Cache::store('redis')->hIncrBy('prize_pool', $selectedPrize, -1);if ($remaining < 0) {// 如果库存不足,恢复库存并返回失败信息Cache::store('redis')->hIncrBy('prize_pool', $selectedPrize, 1);return json(['status' => 'fail', 'message' => 'Sorry, all prizes are gone']);}// 记录用户中奖信息Cache::store('redis')->hSet('user_prizes', $userId, $selectedPrize);return json(['status' => 'success', 'message' => 'Congratulations, you won!', 'prize' => $selectedPrize]);} finally {// 释放锁Cache::store('redis')->del($lockKey);}} else {return json(['status' => 'fail', 'message' => 'System busy, please try again later']);}}
}

3. 解释并发处理逻辑

  • 分布式锁:在抽奖逻辑的开始,我们使用 Redis 的 set 命令来获取一个分布式锁,确保当前操作的唯一性。锁的 nx 参数表示“仅在键不存在时设置键”,ex 参数设置锁的超时时间(这里是5秒)。

  • 获取锁成功:如果当前用户成功获取了锁,则继续执行抽奖逻辑。在抽奖结束后,无论成功与否,都需要释放锁。

  • 获取锁失败:如果获取锁失败,说明有另一个用户正在执行抽奖操作,当前用户会收到系统繁忙的提示。

  • 库存处理:在确定用户抽中某个奖品后,使用 Redis 的  hIncrBy  命令减少该奖品的库存。如果库存不足,会回滚该操作并返回失败信息。

4. 调用抽奖

当用户调用 draw() 方法时,系统会根据设定的百分比概率和库存情况随机选择一个奖品。

5. 可扩展性

  • 抽奖概率控制:在实际场景中,你可以通过调整  getRandomPrize()  函数中的逻辑来实现,例如通过给奖品分配比例、权重等。

  • 多用户并发:Redis 的原子操作能够确保在高并发情况下奖品库存的正确性,因此在并发抽奖场景中也可以安全使用。

  • 过期管理:可以为奖品池设置过期时间,避免长期占用内存。用户中奖信息也可以设置过期时间,防止数据堆积。

总结

通过引入 Redis 分布式锁,确保了在高并发情况下抽奖的原子性和安全性。这样可以有效防止多个用户同时抽中同一个奖品导致库存不足的问题,并且提高了系统的稳定性。


http://www.ppmy.cn/devtools/108533.html

相关文章

机器人外呼有哪些优势?

机器人外呼&#xff0c;作为一种结合了计算机技术和人工智能技术的自动化工具&#xff0c;具有多重显著优势。以下是其主要优势的详细阐述&#xff1a; ### 1. 高效性 * **大幅提升工作效率**&#xff1a;机器人外呼可以全天候、不间断地进行工作&#xff0c;不受时间、地点和…

【文献及模型、制图分享】基于多尺度融合的旅游生态承载力评估——以江苏省沿海县域为例

文献介绍 正确认识旅游生态承载力是指导区域旅游可持续发展的关键。提出“多尺度融合”评价单元划分方法&#xff0c;引入“载体—载荷”视角创建新的旅游生态承载力评价体系&#xff0c;并以江苏省沿海县域为例&#xff0c;对所建立的评价单元划分和旅游生态承载力评估方法进…

828华为云征文|基于Flexus云服务器X实例的应用场景-部署多功能密码管理器

&#x1f534;大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 先看这里 写在前面密码管理器Vaultwarden密码管理器简介优点部署访问Vaultwarden密码管理器 总结 写在前面 大家好哇&#xff0c;最近看到了华为云828活动&#xff0c;很是心…

存储器映射(STM32F407)

STM32是一个32位单片机&#xff0c;也就是说&#xff0c;其核心处理器的数据总线宽度为32&#xff0c;这意味着它一次可以处理32位的数据。因此&#xff0c;它可以很方便的访问4GB以内的存储空间&#xff08;2^32 4GB&#xff09;。 Cortex-M4内核将STM32F407的系统架构中的所…

华为 HCIP-Datacom H12-821 题库 (7)

有需要题库的可以看主页置顶 V群仅进行学习交流 1.配置 VRRP 跟踪物理接口状态的命令是在华为设备上&#xff0c;以下哪一项是配置 VRRP 跟踪物理接口状态的命令&#xff1f; A、track vrrp vrid 1 interface GigabitEthernet0/0/0 B、vrrp vrid 1 track interface GigabitE…

node.js实现阿里云短信发送

效果图 实现 一、准备工作 1、官网直达网址&#xff1a; 阿里云 - 短信服务 2、按照首页提示依次完成相应资质认证和短信模板审核&#xff1b; 3、获取你的accessKeySecret和accessKeyId&#xff1b; 方法如下&#xff1a; 获取AccessKey-阿里云帮助中心 4、获取SignNa…

[深度学习][LLM]:浮点数怎么表示,什么是混合精度训练?

混合精度训练 混合精度训练1. 浮点表示法&#xff1a;[IEEE](https://zh.wikipedia.org/wiki/电气电子工程师协会)二进制浮点数算术标准&#xff08;IEEE 754&#xff09;1.1 浮点数剖析1.2 举例说明例子 1:例子 2: 1.3 浮点数比较1.4 浮点数的舍入 2. 混合精度训练2.1 为什么需…

《响应式 Web 设计:纯 HTML 和 CSS 的实现技巧》

一、引言 在当今数字化时代&#xff0c;人们使用各种不同的设备访问网页&#xff0c;包括台式电脑、笔记本电脑、平板电脑和智能手机等。为了确保网页在不同设备上都能提供良好的用户体验&#xff0c;响应式 Web 设计变得至关重要。响应式 Web 设计是一种能够根据设备屏幕大小和…