0
点赞
收藏
分享

微信扫一扫

Redis抢红包案例(二倍均值算法)


整体流程


  1. 记(记录谁抢了多少、防止重复抢、如果红包到期没抢完,需要退回)
  2. 红包算法,保证每个红包大致有个范围,大家抢的差不多

需求举例


各种节假日,发红包+抢红包,100%对并发有要求,不能使用mysql一个总的大红包,会有可能拆分成多个小红包,总金额=分金额1+分金额2....分金额N每个人只能抢一次,显示剩余红包个数,需要记录抢的多少以及每个人的抢到时间、总耗时时间,防止作弊红包过期,剩余退回

业务细节考虑

抢红包是不是并发场景?参与的人是不是很多?需不需要加锁?假如是redis,整体流程用什么数据结构来比较合适?

就拿并发场景来考虑,假如用redis,那么就不需要加锁,因为redis本来就是单线程,它不需要加锁就实现了原子性操作,实现了锁的效果。redis特性,高并发、实时、原子性、单线程。

首先,发红包

redis key用list结构,如redPackage:1,把红包的一些数额通过计算push进list

第二,抢红包 

redis rpop,先到先得

难点

一、拆分算法

红包其实就是金额,拆分算法如何?给你100,分成10个小红包(金额有可能部分相同),如何随时拆分每个放置的额度。每个人至少抢一分钱

二、限制次数

每个人只能抢一次

三、原子性

每抢走一个红包就减小一个(类似库存)

二倍均值算法

剩余红包金额为M,剩余人数为N,那么有如下公式:

每次抢到的金额 = 随机区间 (0, M / N X 2),php就是 rand(0, M / N X 2),注意,最好把0替换成0.01,以免随机到0,这里我没做测试,是从其他网友帖子里看到的,本人没做测试,有机会测试一下

这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。

举个栗子:

假设有10个人,红包总额100元。100/10X2 = 20, 所以第一个人的随机范围是(0,20 ),平均可以抢到10元。

假设第一个人随机到10元,那么剩余金额是100-10 = 90 元。90/9X2 = 20, 所以第二个人的随机范围同样是(0,20 ),平均可以抢到10元。

假设第二个人随机到10元,那么剩余金额是90-10 = 80 元。80/8X2 = 20, 所以第三个人的随机范围同样是(0,20 ),平均可以抢到10元。

以此类推,每一次随机范围的均值是相等的。

//php代码

<?php

//红包金额100元,分10次发送

//2位小数的随机数
function randomFloat($min = 0, $max = 1)
{
$num = $min + mt_rand() / mt_getrandmax() * ($max - $min);
return sprintf("%.2f", $num);
}


//公用函数
function pingjunhongbao($total_money, $total_times)
{


$number = $total_times;

for ($i = 1; $i <= $total_times; $i++) {


if ($i == $total_times) {
$red_bao = $total_money;
} else {
echo "total_money==" . $total_money;
echo "<br>";
echo "number==" . $number;
echo "<br>";
echo "total_money/total_times* 2==" . $pingjun = round($total_money / $number * 2, 2);
echo "<br>";
echo '随机0.01和' . $pingjun . '之间两位小数的值是:' . randomFloat(0.01, $pingjun);
echo "<br>";
$red_bao = round(randomFloat(0.01, $total_money / $number * 2), 2);
}


$total_money -= $red_bao;
$number -= 1;

echo "<br><br><br>";
print_r("第{$i}个人抢到{$red_bao}元红包,剩余红包{$total_money}元");
echo "<br>";
}


}

$total_money = 100;
$total_times = 10;

echo "共计" . $total_money . "元钱,分给" . $total_times . "个人";
echo "<hr>";
pingjunhongbao($total_money, $total_times);

?>
//结果

共计100元钱,分给10个人total_money==100
number==10
total_money/total_times* 2==20
随机0.01和20之间两位小数的值是:6.57


第1个人抢到0.59元红包,剩余红包99.41元
total_money==99.41
number==9
total_money/total_times* 2==22.09
随机0.01和22.09之间两位小数的值是:18.64


第2个人抢到13.18元红包,剩余红包86.23元
total_money==86.23
number==8
total_money/total_times* 2==21.56
随机0.01和21.56之间两位小数的值是:11.41


第3个人抢到7.26元红包,剩余红包78.97元
total_money==78.97
number==7
total_money/total_times* 2==22.56
随机0.01和22.56之间两位小数的值是:15.14


第4个人抢到21.95元红包,剩余红包57.02元
total_money==57.02
number==6
total_money/total_times* 2==19.01
随机0.01和19.01之间两位小数的值是:13.34


第5个人抢到0.7元红包,剩余红包56.32元
total_money==56.32
number==5
total_money/total_times* 2==22.53
随机0.01和22.53之间两位小数的值是:14.43

第6个人抢到2.3元红包,剩余红包54.02元
total_money==54.02
number==4
total_money/total_times* 2==27.01
随机0.01和27.01之间两位小数的值是:1.63


第7个人抢到4.14元红包,剩余红包49.88元
total_money==49.88
number==3
total_money/total_times* 2==33.25
随机0.01和33.25之间两位小数的值是:30.49


第8个人抢到27.25元红包,剩余红包22.63元
total_money==22.63
number==2
total_money/total_times* 2==22.63
随机0.01和22.63之间两位小数的值是:19.09


第9个人抢到7.97元红包,剩余红包14.66元

第10个人抢到14.66元红包,剩余红包0元


存储记录

至少有三个数据源保存

mysql、redis、mq。

为什么包含mq?因为在高并发场景下,临时存储数据的过度数据库就是mq,也不绝对使用。




举报

相关推荐

0 条评论