php基于redis的商品秒杀、抢购是怎么实现的?

老头子

文章最后更新时间:2022年01月27日

现在商品秒杀和抢购这样的需求还是挺多的,当然实现这样需求的方法还是挺多的,解决这样的需求其实就是解决商品超卖问题,如果说用户量不大的情况下,商品超卖的问题出现的概率就会很小。但是避免被“灰产党”薅羊毛还是要重视一下这个问题的。

什么是商品超卖?

这个问题其实很好理解,商品超卖说的就是说在同一时间有大量的用户来购买活动商品(一般是打折的商品),本来商品库存只有100,最后却卖出了120件商品,对商家会造成一定的经济损失。那么为什么会出现这样的问题呢?这个其实也很好理解,我们正常情况下的程序逻辑是,用户下单前我们会先检测库存,当库存数量不是0的时候,那么该用户就可以正常下单付款了,用户下单之后我们再把库存数量减1。那么商品超卖的问题就是由检测库存来的,当在高并发环境下,同一时间有大量用户来购买商品,当程序还未把库存数量减1的时候,就有其他用户又来了,这时候程序返回的依然是有库存(程序还没来得及减库存),这样的话超卖问题就产生了。 

下面我们一起来研究研究php是怎么基于redis的实现商品秒杀、抢购的。 

一、准备工作:

 1、数据库建表 

我们会先创建两张表,一张是商品表就是相当于用户秒杀的商品,另外一张是订单表,用户记录用户的抢购订单。 

CREATE TABLE `goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `stock` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` varchar(100) DEFAULT NULL,
  `rank` int(11) DEFAULT NULL,
  `time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

两张表创建完成之后,我们先向商品表goods插入一条数据:INSERT INTO `goods` (`name`,`stock`) VALUES ('鼠标',100);   

然后后面会使用ab压测工具来模拟高并发环境,所以ab压测工具也要先准备好。 

二、php基于redis的商品秒杀、抢购 

首先先简单说一下php基于redis实现商品秒杀或者抢购的原理吧,其实就是使用redis队列,因为pop操作是原子的,即使有很多用户同时到达,也是依次执行。我们先把要秒杀的商品库存存入到redis列表中,当有一位用户秒杀成功之后,我们使用lpop取出1,直到取完也就是相当于秒杀结束。 

模拟并发的第一步:先将库存存入到redis的list列表中 

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$stock = 20;//库存
for ($i = 0; $i < $stock; $i++) {
$redis->lpush('hot_goods', 1);
}

执行上面代码,我们会在redis存入一个键为hot_goods的列表。 

第二步:秒杀、抢购的代码 

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$conn = mysqli_connect('localhost', 'root', '123456', 'study');
if (!$conn) {
die("连接失败: " . mysqli_connect_error());
}

$uid = uniqid(); //生成一个用户id
if (!$redis->lpop('hot_goods')) {
mysqli_query($conn, "UPDATE goods SET stock =0 where id=1 "); //数据库商品秒杀库存更新为0
return '秒杀已经结束!';
}

mysqli_query($conn, "INSERT into `order` (`uid`,`rank`) values('" . $uid . "'," . $redis->llen('hot_goods') . ")"); //生成订单记录
mysqli_close($conn);

这段代码很简单,就是当用户来的时候先从hot_goods列表中取值,取出成功就代表秒杀或者抢购成功了,我们把该用户的id和其他信息存入到订单记录表中,当hot_goods列表中没有值的时候,代表秒杀结束了,我们将数据库中的商品库存更新为0。 

第三步:利用压测工具模拟高并发 

执行命令:ab -n 1000 -c 1000 http://localhost/test.php

该命令是指并发数1000,请求次数也是1000。执行完之后我们查看一下mysql数据库中order订单表中产生的记录:

可以看到正好产生了20条记录(请忽略id),没有出现超卖。当然也测试过并发数10000的情况,同样没有出现超卖。

php基于redis的实现的商品秒杀、抢购大致的原理就是这样了。当然上面的代码还有很多不完善的地方,比如没订单插入数据库是否成功,还有秒杀结束的时候会重复更新库存为0等等。不过这些不是这篇文章的重点,这篇文章最多起个“启蒙”的作用吧。 

文章版权声明:除非注明,否则均为老头子博客原创文章,转载或复制请以超链接形式并注明出处。

您需要 登录账户 后才能发表评论

发表评论

快捷回复: 表情:
评论列表 (暂无评论,1549人围观)

还没有评论,来说两句吧...

取消
微信二维码
微信二维码
支付宝二维码