问题

一般来说,秒杀或抢购这种高并发下我们注意会遇到两个问题

  • 高并发操作对数据库的压力
  • 商品超卖的问题

商品超卖的问题我们一般可以用锁解决,不了解的话可以看看 少量并发的解决方案 — 锁
高并发对于数据库的压力我们也可以用队列来解决 队列的简单说明

在 队列的简单说明 一文中,我们知道要先把商品全部入列完成才能进行出列操作,这样那就有问题了,如果商品很多,或买的人很少,那么商品是不是要等很久才能全部入列完呢,这显然是不行的。那么有什么办法解决呢,当然是有,我们继续来看下面:

解决

这里我们需要三个页面

  • 商品全部入列页面 ready.php
  • 订单入列页面(下单) order.php
  • 订单入库页面 toDb.php

思路:
先把库存加入到redis,如:有200个库存就在list列表加200条记录,然后就是下单了,有一个人下单,就从库存列表取出一条,并判断有没有数据,没有数据就表示没有库存了,有数据就加入另一个list列表,如:order_list,然后就是加入到数据库了,我们从 order_list 列表 取出加入到数据库。这样就解决了商品买的人少,或者库存过多导致的边入列边出列的问题。下面我们就看看具体的代码吧

商品全部入列页面

ready.php

  1. <?php
  2. //准备秒杀 将商品库存入列
  3. $redis = new redis();
  4. $redis->connect('127.0.0.1',6379);
  5. //先把商品入列 - 假如有200个库存
  6. for ($i = 1; $i <= 200; $i++) {
  7. //入列
  8. $redis->rPush('stock_list', 1);//插入在最右边
  9. }
  10. 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秒钟一次,所以数据库是完全不会有任何压力的