php+redis一步一步測試秒殺


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM