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

2021-09-06 10:05:04 浏览数 (1)

前期准备

  • 背景 相信很多在小公司打拼的小伙伴 对于秒杀系统真的是可遇不可求 我们只能通过模拟演练 一方面熟悉高并发场景、提升编码技能 另一方面,为进入大厂做好准备 此处,我主要还是阐述下设计思路 有不同见解,欢迎指摘 …
  • 模拟环境 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

代码语言:javascript复制
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 等代码的设限 以及流量削峰、限流等操作 最后还要有薅羊毛等恶意刷单的考量、人工最终审核等等 …
  • 个人拙见,谢谢 …

0 人点赞