1.個人環境是windows下的phpstudy,進入到apache/bin目錄裏面有個ab.exe,壓力測試命令以下php
- ./ab.exe -c 200 -n 1000 http://192.168.1.244/mysql.php
2.mysql.php代碼以下
正常的邏輯思惟,壓力增大後,致使數據庫num字段成爲負數,將下面代碼粘貼到本身網站下測試便可。html
在test數據庫下,新建一個num(庫存)的表,id字段int類型主鍵自增,num字段int類型python
新建一個goods_order(訂單)的表,id字段int主鍵自增,goods_id字段int,user_id字段int類型,mysql
若是發現num字段沒法成爲負數,打開sleep(2);訪問量堆積起來便可linux
- <?php
- header("Content-type: text/html; charset=utf-8");
-
- $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';"));
- 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);
-
- if($count['num']>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.若是開啓redis
- $sql="update num set num=num-1 where id=1 and (num -1 ) >= 0";
通過測試num字段最小爲0,在壓力測試下代碼運行正常,sql
想要增大ab壓力併發量測試,數據庫
- ./ab.exe -c 500 -n 1000 http://192.168.1.244/mysql.php
會出現以下(看來要去Linux下搭建apache來測試了)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壓力測試槓槓的,不會報錯了windows
- ./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->get("num"));
- $count=(int)$num;
- echo "總共有:".$count;
- echo "<br/>";
- if($count>0){
-
- $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
-
- 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){
-
- $redis->set("num",$num-1);
- $redis->incr("order");
- $exec = $redis->exec();
-
-
- if($exec[0]==true){
- echo "搶購成功,還剩:".($count-1);
- }else{
- echo "很不幸,沒搶到,能夠再搶一把";
- }
- }else{
- echo "活動結束";
- }
-
-
- ?>
設置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");
-
- $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';"));
- 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);
-
- if($count['num']>0){
- try{
- $dbh->beginTransaction();
-
- $sql="update num set num=num-1 where id=1 and (num -1 ) >= 0";
- $row = $dbh->exec($sql);
- if (!$row)
- throw new PDOException('搶購失敗,再搶');
- $sql2="insert into goods_order (goods_id,user_id) values(1,123456789)";
- $row = $dbh->exec($sql2);
- 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/>");
- }
7.總結,以上只是解決了秒殺時候,超賣的問題,可是並未考慮秒殺效率,只是作了簡單測試,後期會加入隊列機制處理秒殺