PHP 使用 redis 进行商品秒杀设计思路

1 篇文章 0 订阅

前期准备

  • 背景

    相信很多在小公司打拼的小伙伴
    对于秒杀系统真的是可遇不可求
    我们只能通过模拟演练

    一方面熟悉高并发场景、提升编码技能
    另一方面,为进入大厂做好准备
    此处,我主要还是阐述下设计思路
    有不同见解,欢迎指摘 …

  • 模拟环境

    PHP7.2、CentOS7.9、Redis6.0.8、ab 压测工具

☛ 设计思路

  • 首先,要明确的一点是,不能直接按照传统商品订单思路处理,毕竟大流量下不能丢失用户美好的交互性
    然后,准备秒杀服务器,不影响主业务运行
    用户在秒杀等待页面,使用 ajax 异步更新倒计时

  • 点击"抢购"触发时
    使用 Redis 开启事务
    提取用户唯一标识 ID,首先集中到 redis 的一个商品数量的集合"kill_user_que"
    然后,将符合要求的 用户ID ,存入秒杀队列"kill_user"
    注意商品数量的递减变化
    最终的结果是得到一个,不会超售商品数量的 秒杀队列(kill_user)

  • 设置一个或多个线程,也可以是定时任务
    去秒杀队列(kill_user) 中提取 用户ID,依次执行下单逻辑

具体的业务处理,要根据实际场景,再做代码优化 … 推荐文章 —— 【用 Redis 轻松实现秒杀系统】


测试参考

☛ 秒杀处理代码参考

假定要抢购的商品数量为 100 件,即 "kill_num" 要提前设置为 100

public function testFastSale(){
       $redis2 = new \Redis();
       $redis2->connect('192.168.80.224',6379);

       $killNumSet = 100;
       //初始化设置秒杀商品数量
       //$redis2->set('kill_num',$killNumSet);
       
       //模拟发起请求的用户ID
       $userID = rand(1111,2222);

       $killNum = $redis2->get('kill_num');
       if ($killNum > 0){
           //TODO 此时,还有商品可进行抢购
           if ($redis2->sIsMember('kill_user_que',$userID)){
               //TODO 此时说明用户已经抢到了
               $message = 'Sorry,一个账号只能抢一件!';
           }else{
               $countCanBuyer = $redis2->sCard('kill_user_que');
               if ($countCanBuyer >= $killNumSet){
                   $message = 'Sorry,当前排队已满员!';
               }else{
                   $redis2->watch('kill_num','kill_user','kill_user_que');
                   $redis2->multi(); //开启事务
                   $redis2->sAdd('kill_user_que',$userID); //加入集合
                   $redis2->decr('kill_num'); //商品数量减一
                   $redis2->rPush('kill_user',$userID);//将用户有序的压入队列
                   $redis2->exec(); //执行事务
                   $message = "恭喜,抢购成功!";
               }
           }
       }else{
           $message = "Sorry,商品已售完!";
       }
       return $message;
   }

【提示】:

  • 为了避免同一用户多抢商品,我使用的是集合 "kill_user_que"
  • 而对符合抢购的用户ID,使用队列 "kill_user" 进行存储 (有序性)
    方便后期,对队列的弹出操作(POP),后续下单业务处理

建议,参考文档 —— Redis 事务|【菜鸟教程】

☛ ab 模拟高并发请求

  • 在此,我使用 ab 压力测试工具,模拟高并发的请求场景
    运行命令如下:"ab -c 50 -n 3000 http://tp5pro.com/index/test"
  • 执行完成后,可在 redis 中查看数据("kill_num"、"kill_user"):

后续逻辑,就要见仁见智了 …

附录

总觉得写得太少,那就多做一点补充吧!

▶ 为什么我设置一个集合、一个队列?

  • 一开始,我只是想到使用一个队列 "kill_user"就好
    但是,我发现:
    如果同一个用户账号,可能不止一次能抢到商品
    这在正常业务中,一般是不允许的

  • 为了保证用户的唯一性,想到了改用集合
    但是,集合有个缺点:不能记录顺序
    为了业务合理性
    先排到的人自然会先下订单
    虽然,也存在有序集合(sorted set)可以满足需求
    但是,分析后发现会有很多关于排序取值的逻辑处理,很是繁杂

  • 此时想到
    首先,使用一个集合"kill_user_que",负责前期对 用户ID 的筛选,保证唯一性
    然后,将刷选符合要求的 用户ID 压入一个队列"kill_user"
    后期,只需操作队列 "kill_user" 中的数据即可

如此一来,就出现了这种情况,当然这里主要是提供一种解决思路 !

▶ 可不是仅仅有了代码就行了!

对于秒杀类的需求,需要考虑的方面会比较多,可不只有编码

  • 一般来说
    秒杀最容易引来用户流量(小项目没有客户群,那就么啥讨论性了)
    可能要考虑 Redis 集群的部署、负载均衡、带宽等支持
    其次,还有前端页面静态化、ajax 等代码的设限
    以及流量削峰、限流等操作
    最后还要有薅羊毛等恶意刷单的考量、人工最终审核等等 …

  • 个人拙见,谢谢 …

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值