PHP秒杀系统设计

场景描述

假设现在要搞一个特价促销活动,商品为iPhone11手机,库存为2台,购买时间为晚上8点。预计有100名用户参与这次促销活动。现在用PHP做了一个链接,时间一到,谁先点击,谁就可以下单购买。

表设计

  • 商品库存表
CREATE TABLE `goods` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `subject` varchar(100) DEFAULT NULL,
  `stock` int(3) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

INSERT INTO `goods`(`id`, `subject`, `stock`) VALUES (1, 'iPhone11', 2);
  • 订单表
CREATE TABLE `good_orders` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` varchar(36) DEFAULT NULL,
  `good_id` int(11) DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

PHP代码

PHP伪代码如下:

$sql = "SELECT * from goods where id = 1";
$userid = uniqid();
$row = $db->query($sql);
if ($row['stock'] > 0) {
    //插入订单表
    $db->exec("insert into good_orders set user_id ='{$userid}',good_id='1'");
    //减库存
    $stock = $row['stock'] - 1;
    $db->exec("update goods set stock ='{$stock}' where good_id='1'");
    echo '购买成功';
} else {
    echo '库存不足';
}

不在并发条件下,单个点击是没有问题的,good_orders表里只会出现2条订单记录,但一旦处于并发条件下,就会存在多条订单,这样就造成了超卖。

文件锁

$fp = fopen("lock.txt", "w+");
if (flock($fp, LOCK_EX)) { // 进行排它型锁定
    $sql = "SELECT * from goods where id = 1";
    $userid = uniqid();
    $row = $db->query($sql);
    if ($row['stock'] > 0) {
        //插入订单表
        $db->exec("insert into good_orders set user_id ='{$userid}',good_id='1'");
        //减库存
        $stock = $row['stock'] - 1;
        $db->exec("update goods set stock ='{$stock}' where good_id='1'");
        echo '购买成功';
    } else {
        echo '库存不足';
    }
    flock($fp, LOCK_UN); // 释放锁定
} else {
    echo '前方拥挤';
}
fclose($fp);

redis

将库存放入redis

$redis = new \Redis;
$redis->connect('127.0.0.1', 6379);
$sql = "SELECT * from goods where id = 1";
$userid = uniqid();
$row = $db->query($sql);
if ($row['stock'] > 0) {
    for ($i = 0; $i < $row['stock']; $i++) {
        $redis->lpush('goods_number', $i);
    }
}
echo $redis->llen('goods_number');

从redis中扣库存

$stock = $this->redis->llen('goods_number');
$count = $this->redis->rpop('goods_number');  //下单时做rpop 从goods_number中取出1
if ($count===false) {
    echo '库存不足';exit();
}
$userid = uniqid();
//插入订单表
$db->exec("insert into good_orders set user_id ='{$userid}',good_id='1'");
//减库存
$stock = $row['stock'] - 1;
$db->exec("update goods set stock ='{$stock}' where good_id='1'");
echo '购买成功';

发表评论

邮箱地址不会被公开。 必填项已用*标注