PHP秒杀系统设计实现代码
场景描述
假设公司现在要搞一个特价促销活动,商品为iPhone11手机,库存为2台,购买时间为晚上8点。预计有100名用户参与这次促销活动。现在用PHP做了一个链接,时间一到,谁先点击,谁就可以下单购买。
表设计
商品库存表
1 2 3 4 5 6 7 |
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); |
订单表
1 2 3 4 5 6 7 |
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伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$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条订单记录,但一旦处于并发条件下,就会存在多条订单,这样就造成了超卖的情况,老板亏了咱的日子也不好过。
使用文件锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$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
1 2 3 4 5 6 7 8 9 10 11 |
$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中扣库存
1 2 3 4 5 6 7 8 9 10 11 12 |
$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 '购买成功'; |