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


    整体流程

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

    需求举例

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

    业务细节考虑

    抢红包是不是并发场景?参与的人是不是很多?需不需要加锁?假如是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,也不绝对使用。

  • 相关阅读:
    eclipse快捷键
    ideaIU-2017.3.2版本的免费安装以及2020版本破解
    并发相关问题以及java基础知识
    飞秋软件-局域网内互传
    视频解析网站
    bzoj2458: [BeiJing2011]最小三角形
    bzoj3170: [Tjoi2013]松鼠聚会
    bzoj5056:OI游戏
    dtoj#4224. 小L的占卜
    dtoj#4222. 小b爱旅行(travel)
  • 原文地址:https://www.cnblogs.com/wt645631686/p/9719221.html
Copyright © 2020-2023  润新知