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');