问题
一般来说,秒杀或抢购这种高并发下我们注意会遇到两个问题
- 高并发操作对数据库的压力
- 商品超卖的问题
商品超卖的问题我们一般可以用锁解决,不了解的话可以看看 少量并发的解决方案 — 锁 ,
高并发对于数据库的压力我们也可以用队列来解决 队列的简单说明
在 队列的简单说明 一文中,我们知道要先把商品全部入列完成才能进行出列操作,这样那就有问题了,如果商品很多,或买的人很少,那么商品是不是要等很久才能全部入列完呢,这显然是不行的。那么有什么办法解决呢,当然是有,我们继续来看下面:
解决
这里我们需要三个页面
- 商品全部入列页面 ready.php
- 订单入列页面(下单) order.php
- 订单入库页面 toDb.php
思路:
先把库存加入到redis,如:有200个库存就在list列表加200条记录,然后就是下单了,有一个人下单,就从库存列表取出一条,并判断有没有数据,没有数据就表示没有库存了,有数据就加入另一个list列表,如:order_list,然后就是加入到数据库了,我们从 order_list 列表 取出加入到数据库。这样就解决了商品买的人少,或者库存过多导致的边入列边出列的问题。下面我们就看看具体的代码吧
商品全部入列页面
ready.php
<?php//准备秒杀 将商品库存入列$redis = new redis();$redis->connect('127.0.0.1',6379);//先把商品入列 - 假如有200个库存for ($i = 1; $i <= 200; $i++) {//入列$redis->rPush('stock_list', 1);//插入在最右边}echo $redis->lLen('stock_list');
订单入列页面(下单)
order.php
<?php
$redis = new redis();
$redis->connect('127.0.0.1',6379);
//库存列(stock_list)取出
$stock = $redis->lPop('stock_list');//从最左边取出
//判断有没有库存
if (empty($stock)){
return json_encode(['code'=>200,'msg'=>'商品抢完啦']);
}
$user_id = time() . mt_rand(1000,9999);
//把下单的用户id入列到 order_list
$redis->rPush('order_list', $user_id);//插入在最右边
订单入库页面
toDb.php
<?php
//订单列(order_list) 入 数据库
$redis = new redis();
$redis->connect('127.0.0.1',6379);
$user_id = $redis->lPop('order_list');//从最左边取出
if (empty($user_id)){
return false;
}
//订单信息
$order_Data = [
'user_id' => $user_id,
'goods_id'=> 112,
];
$dsn = sprintf("mysql:host=%s;dbname=%s;charset=utf8", 'localhost', 'test');
$db_user = 'root';
$db_pass = 'root';
//指定查询结果集为关联数组
$option = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC);
$pdo = new PDO($dsn, $db_user, $db_pass, $option);
$sql = 'insert into `order` (`user_id`,`goods_id`) values (:user_id,:goods_id)';
$sth = $pdo->prepare($sql);
$sth->bindValue(':user_id',$order_Data['user_id']);
$sth->bindValue(':goods_id',$order_Data['goods_id']);
$sth->execute();
测试
好了,代码写完了,我们来试一下
首先是秒杀前的准备工作(将商品库存入列),
php ready.php
把入库加入定时任务
*/1 * * * * root php /wwwroot/toDb.php
假设我们的下单页面地址是 http://local.test.cn/order.php ,这里我们使用 ab 工具模拟高并发
ab -n 1200 -c 1200 http://local.test.cn/order.php
好了,我们看到在1200并发下,我们是把数据全部加入到 order_list列表中的,然后我们加入到数据库是1秒钟一次,所以数据库是完全不会有任何压力的
