1、普通的秒杀查库减库存:
<?php /** 100个用户同时访问100次会出现超卖 **/ //连接数据库 $dsn = "mysql:host=localhost;dbname=ceshi"; $db = new PDO($dsn, 'root', 'root'); //价格 $price=10; //用户id $user_id=1; //商品id $goods_id=1; //skuid $sku_id=11; //数量 $number=1; //生成唯一订单 function build_order_no(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } //库存是否大于0 $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'"; $row=$db->query($sql)->fetch(); if($row['number']>0){//高并发下会导致超卖 $order_sn=build_order_no(); //生成订单 $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; $order_rs=$db->exec($sql); //库存减少 $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'"; $store_rs=$db->exec($sql); if($store_rs){ echo "库存减少成功"; }else{ echo "库存减少失败"; } }else{ echo "库存不够"; } ?>
2、把数据库的库存字段设置为无符号:
<?php /** 100个用户同时访问600次的情况下会出现超卖 **/ //连接数据库 $dsn = "mysql:host=localhost;dbname=ceshi"; $db = new PDO($dsn, 'root', 'root'); //价格 $price=10; //用户id $user_id=1; //商品id $goods_id=1; //skuid $sku_id=11; //数量 $number=1; //生成唯一订单 function build_order_no(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } //库存是否大于0 $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id'"; $row=$db->query($sql)->fetch(); if($row['number']>0){//高并发下会导致超卖 $order_sn=build_order_no(); //生成订单 $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; $order_rs=$db->exec($sql); //库存减少 $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'"; $store_rs=$db->exec($sql); if($store_rs){ echo "库存减少成功"; }else{ echo "库存减少失败"; } }else{ echo "库存不够"; } ?>
3、采用排它锁解决:
<?php /** 100个用户同时访问1500次的情况下会出现超卖 **/ //连接数据库 $dsn = "mysql:host=localhost;dbname=ceshi"; $db = new PDO($dsn, 'root', 'root'); //价格 $price=10; //用户id $user_id=1; //商品id $goods_id=1; //skuid $sku_id=11; //数量 $number=1; //生成唯一订单 function build_order_no(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } //库存是否大于0 $sql="select number from ih_store where goods_id='$goods_id' and sku_id='$sku_id' FOR UPDATE"; $row=$db->query($sql)->fetch(); if($row['number']>0){//高并发下会导致超卖 $order_sn=build_order_no(); //生成订单 $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; $order_rs=$db->exec($sql); //库存减少 $db->beginTransaction();//开启事务处理 $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'"; $store_rs=$db->exec($sql); if($store_rs){ echo "库存减少成功"; $db->commit(); }else{ echo "库存减少失败"; } }else{ $db->rollBack(); echo "库存不够"; } ?>
4、采用redis队列实现
第一步把库存存入队列中
<?php //500个库存存入redis队列 $store=20; $redis=new Redis(); $result=$redis->connect('127.0.0.1',6379); $res=$redis->llen('goods_store'); $count=$store-$res; for($i=0;$i<$count;$i++){ $redis->lpush('goods_store',1); } echo $redis->llen('goods_store'); ?>
队列操作:
<?php /** 可以抗住任何并发 **/ //连接数据库 $dsn = "mysql:host=localhost;dbname=ceshi"; $db = new PDO($dsn, 'root', 'root'); //价格 $price=10; //用户id $user_id=1; //商品id $goods_id=1; //skuid $sku_id=11; //数量 $number=1; //生成唯一订单 function build_order_no(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } //连接redis $redis=new Redis(); $result=$redis->connect('127.0.0.1',6379); // echo $redis->llen('goods_store'); // die; $count=$redis->lpop('goods_store'); if(!$count){ echo "redis队列无库存"; return false; } //生成订单 $order_sn=build_order_no(); $sql="insert into ih_order(order_sn,user_id,goods_id,sku_id,price) values('$order_sn','$user_id','$goods_id','$sku_id','$price')"; $order_rs=$db->exec($sql); //库存减少 $sql="update ih_store set number=number-{$number} where sku_id='$sku_id'"; $store_rs=$db->exec($sql); if($store_rs){ echo "库存减少成功"; }else{ echo "库存减少失败"; } ?>
ab测试:
ab -n 2000 -c 100 http://localhost/miaosha/index2.php
数据库导入
-- ---------------------------- -- Table structure for `ih_goods` -- ---------------------------- DROP TABLE IF EXISTS `ih_goods`; CREATE TABLE `ih_goods` ( `goods_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `cat_id` int(11) NOT NULL, `goods_name` varchar(255) NOT NULL, PRIMARY KEY (`goods_id`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of ih_goods -- ---------------------------- INSERT INTO `ih_goods` VALUES ('1', '0', '小米手机'); -- ---------------------------- -- Table structure for `ih_order` -- ---------------------------- DROP TABLE IF EXISTS `ih_order`; CREATE TABLE `ih_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_sn` char(32) NOT NULL, `user_id` int(11) NOT NULL, `status` int(11) NOT NULL DEFAULT '0', `goods_id` int(11) NOT NULL DEFAULT '0', `sku_id` int(11) NOT NULL DEFAULT '0', `price` float NOT NULL, `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 COMMENT='订单表'; -- ---------------------------- -- Table structure for `ih_store` -- ---------------------------- DROP TABLE IF EXISTS `ih_store`; CREATE TABLE `ih_store` ( `id` int(11) NOT NULL AUTO_INCREMENT, `goods_id` int(11) NOT NULL, `sku_id` int(10) unsigned NOT NULL DEFAULT '0', `number` int(10) unsigned NOT NULL DEFAULT '0', `freez` int(11) NOT NULL DEFAULT '0' COMMENT '虚拟库存', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='库存'; -- ---------------------------- -- Records of ih_store -- ---------------------------- INSERT INTO `ih_store` VALUES ('1', '1', '11', '20', '0');