1.我的環境是windows下的phpstudy,進入到apache/bin目錄裏面有個ab.exe,壓力測試命令如下
./ab.exe -c 200 -n 1000 http://192.168.1.244/mysql.php
2.mysql.php代碼如下
正常的邏輯思維,壓力增大後,導致數據庫num字段成爲負數,將下面代碼粘貼到自己網站下測試即可。
在test數據庫下,新建一個num(庫存)的表,id字段int類型主鍵自增,num字段int類型
新建一個goods_order(訂單)的表,id字段int主鍵自增,goods_id字段int,user_id字段int類型,
如果發現num字段無法成爲負數,打開sleep(2);訪問量堆積起來即可
<?php
header("Content-type: text/html; charset=utf-8");
//pdo連接數據庫方法
$dbms='mysql'; //數據庫類型
$host='localhost'; //數據庫主機名
$dbName='test'; //使用的數據庫
$user='root'; //數據庫連接用戶名
$pass='root'; //對應的密碼
$dsn="$dbms:host=$host;dbname=$dbName";
try {
$dbh = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8';")); //初始化一個PDO對象
echo "連接成功<br/>";
$sq="select num from num where id=1";
$rs=$dbh->query($sq);
$rs->setFetchMode(PDO::FETCH_ASSOC);
$count = $rs->fetch();
print_r($count);
//sleep(2); 如果壓力不夠釋放此行代碼,讓訪問量堆積,數據庫num字段成負數
if($count['num']>0){
//$sql="update num set num=num-1 where id=1 and (num -1 ) >= 0";//開啓此行,註釋下行,無法破
$sql="update num set num=num-1 where id=1";
echo $sql;
$count = $dbh->exec($sql);
$sql2="insert into goods_order (goods_id,user_id) values(1,123456789)";
echo $sql2;
$count2 = $dbh->exec($sql2);
echo "購買成功<br />";
}
$dbh = null;
} catch (PDOException $e) {
die ("Error!: " . $e->getMessage() . "<br/>");
}
3.如果開啓
$sql="update num set num=num-1 where id=1 and (num -1 ) >= 0";
經過測試num字段最小爲0,在壓力測試下代碼運行正常,
想要增大ab壓力併發量測試,
./ab.exe -c 500 -n 1000 http://192.168.1.244/mysql.php
會出現如下(看來要去linux下搭建apache來測試了)
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.1.244 (be patient)
Completed 100 requests
Total of 125 requests completed
Test aborted after 10 failures
apr_socket_connect(): ▒▒▒▒Ŀ▒▒▒▒▒▒▒▒▒▒▒ܾ▒▒▒▒▒▒▒▒ӡ▒ (730061)
4.redis測試,搭建好linux下的apache後,訪問linux下的redis.php壓力測試槓槓的,不會報錯了
./ab.exe -c 1000 -n 1000 http://192.168.1.233/redis.php
此壓力下,redis數據正常,不會出現負數
<?php
header("content-type:text/html;charset=utf-8");
$redis = new redis();
$result = $redis->connect('192.168.1.233',"6379");
//$num = $redis->set("num",5);
//die;
$num = ($redis->get("num"));
$count=(int)$num;
echo "總共有:".$count;
echo "<br/>";
if($count>0){
//sleep(2);
$redis->set("num",$num-1);
}else{
}
var_dump($num);
?>
5.以上是ab測試,現在分析代碼
先分析redis.php,上面的這個實現在只有一個客戶端的時候可以執行得很好。 但是, 當多個客戶端同時對同一個鍵進行這樣的操作時, 就會產生競爭條件。舉個例子, 如果客戶端 A 和 B 都讀取了鍵原來的值, 比如 2, 那麼兩個客戶端都會將鍵的值設爲 1 , 但正確的結果應該是 0 纔對。
有了 WATCH , 我們就可以輕鬆地解決這類問題了:
因爲redis的性能很高,當num爲2時,ab模擬兩個併發量後num爲1,模擬兩個併發量和watch如下:
./ab.exe -c 2 -n 2 http://192.168.1.233/redis.php
修復代碼如下,加入watch監聽,確保數據準確性
<?php
//set('num');可以在終端執行
header("content-type:text/html;charset=utf-8");
$redis = new redis();
$result = $redis->connect('192.168.1.233',"6379");
$redis->watch("num");
$num = ($redis->get("num"));
$redis->multi();
$count=(int)$num;
echo "總共有:".$count;
echo "<br/>";
if($count>0){
//sleep(2);
$redis->set("num",$num-1);
$redis->incr("order");
$exec = $redis->exec();
//var_dump($exec);
//die;
if($exec[0]==true){
echo "搶購成功,還剩:".($count-1);
}else{
echo "很不幸,沒搶到,可以再搶一把";
}
}else{
echo "活動結束";
}
//var_dump($num);
//$redis->close();
?>
設置1000個庫存,讓1000個人去搶,196個人搶到了,數據很精準
./ab.exe -c 1000 -n 1000 http://192.168.1.233/redis.php
192.168.1.233:6379> set num 1000
OK
192.168.1.233:6379> get num
"804"
192.168.1.233:6379> get order
"196"
6.分析完redis.php,我們來分析mysql.php
因爲 秒殺後庫存+訂單=秒殺前庫存,所以採用也要採用事物來處理
<?php
header("Content-type: text/html; charset=utf-8");
//pdo連接數據庫方法
$dbms='mysql'; //數據庫類型
$host='localhost'; //數據庫主機名
$dbName='test'; //使用的數據庫
$user='root'; //數據庫連接用戶名
$pass='root'; //對應的密碼
$dsn="$dbms:host=$host;dbname=$dbName";
try {
$dbh = new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8';")); //初始化一個PDO對象
echo "連接成功<br/>";
$sq="select num from num where id=1";
$rs=$dbh->query($sq);
$rs->setFetchMode(PDO::FETCH_ASSOC);
$count = $rs->fetch();
print_r($count);
//sleep(2);
if($count['num']>0){
try{
$dbh->beginTransaction(); // 開啓一個事務
//$row = null;
$sql="update num set num=num-1 where id=1 and (num -1 ) >= 0";
$row = $dbh->exec($sql); // 執行第一個 SQL
if (!$row)
throw new PDOException('搶購失敗,再搶'); // 如出現異常提示信息或執行動作
$sql2="insert into goods_order (goods_id,user_id) values(1,123456789)";
$row = $dbh->exec($sql2); // 執行第二個 SQL
if (!$row)
throw new PDOException('搶購失敗,再搶');
$dbh->commit();
}catch(PDOException $ex){
$dbh->rollback(); // 執行失敗,事務回滾
exit($ex->getMessage());
}
}
$dbh = null;
} catch (PDOException $e) {
die ("Error!: " . $e->getMessage() . "<br/>");
}
ab壓力測試
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.