2016-05-07 41 views
0

求解:在第二次測試中引用值似乎是問題。 取而代之的是'VALUES($ id,$ t1 ....)'我需要做'VALUES(「$ id」,「$ t1」.....),在第二次測試中這確實會引發錯誤。只有在手動打印後才能看到。PDO :: bindParam怎麼這麼慢?

嘿,我最近一直在使用bindParam,並注意到我的頁面加載時間急劇增加。

所以我花了幾個小時來診斷問題,似乎bindParam使用很多更多的處理時間,那麼舊的方式方法(詢問直接使用參數)

爲了確保結果有效,我做了兩次測試。

這裏是我的測試:

for ($j = 0; $j < 2; $j++) { 
    unset($t); 
    $dbh = new PDO('mysql:host=localhost;port=3306', 'root', 'root'); 
    $dbh->query('USE Web_Amicrom_HQ'); 
    $t['start'] = microtime(true); 
    for ($i = 0; $i < 50; $i++) { 
     $id = rand(1000000, 9999999); 
     $t1 = string_random(240); 
     $t2 = string_random(240); 
     $t3 = string_random(1000); 
     $ins = $dbh->prepare('INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, now(), :3, :4)'); 
     $ins->bindParam(':1', $id, PDO::PARAM_INT); 
     $ins->bindParam(':2', $t1, PDO::PARAM_STR); 
     $ins->bindParam(':3', $t2, PDO::PARAM_STR); 
     $ins->bindParam(':4', $t3, PDO::PARAM_STR); 
     $ins->execute(); 
    } 
    $t['loop_fact'] = microtime(true); 

    echo "---- with bindParam ----\n"; 
    $str_result_bench = mini_bench_to($t); 
    echo $str_result_bench; // string return 
    echo "\n\n"; 
} 

for ($j = 0; $j < 2; $j++) { 
    unset($t); 
    $dbh = new PDO('mysql:host=localhost;port=3306', 'root', 'root'); 
    $dbh->query('USE Web_Amicrom_HQ'); 
    $t['start'] = microtime(true); 
    for ($i = 0; $i < 50; $i++) { 
     $id = rand(1000000, 9999999); 
     $t1 = string_random(240); 
     $t2 = string_random(240); 
     $t3 = string_random(1000); 
     $ins = $dbh->prepare("INSERT INTO `test` (id, t1, dt, t2, t3) VALUES($id, $t1, now(), $t2, $t3)"); 
     $ins->execute(); 
    } 
    $t['loop_fact'] = microtime(true); 

    echo "---- with parameter in query ----\n"; 
    $str_result_bench = mini_bench_to($t); 
    echo $str_result_bench; // string return 
    echo "\n\n"; 
} 

結果:

---- with bindParam ---- 
total time : 3136.148ms 


---- with bindParam ---- 
total time : 2645.822ms 


---- with parameter in query ---- 
total time : 41.693ms 


---- with parameter in query ---- 
total time : 52.9752ms 

事情我試過了。

更改報價從雙到單人 - 沒有區別

bindParam不帶參數的類型(例如PDO :: PARAM_INT) - 無差異

改變了所有PARAM名稱(如:1到:身份證等) - 沒有區別

這是一個巨大的性能差異,特別是對於少數幾個查詢。

+0

你有它比使用'執行()'使用數組? – Machavity

+0

也許你可以包括所有未知函數,以便我們可以看到如果我們得到相同的結果 – RiggsFolly

+1

第二個循環中的SQL將無法工作! – RiggsFolly

回答

1

問:爲什麼PDO :: bindParam這麼慢?

答:PDO :: bindParam不慢。

在問題中演示的基準測試不是衡量bindParam性能的有效指標。

第二個測試將爲幾乎每個INSERT引發錯誤。這是因爲字符串文字(?)沒有用單引號引起來。我原本以爲這些會被解釋爲數字。 (令人困惑的是,我們沒有看到來自string_random(240)的返回規範,我們沒有看到該函數的定義,甚至沒有看到返回結果的任何示例。)

如果這是返回一個不能表示有效數字文字的240個字符的字符串...基準比較插入的行〜1500個字符(使用bindParam)與插入的行。

我原來的答案(下面)表示對兩個測試的修改,以便它們執行等效功能。這將比bindParam vs bindParam更有效。


原來的答案

爲了提高性能,調用一個時間preparebindParam功能,在進入循環之前。 (這是沒有必要調用相同的SQL語句上多次做準備。)

$sql = 'INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, NOW(), :3, :4)'; 
    $ins = $dbh->prepare($sql); 
    $id = 0; 
    $t1 = 0; 
    $t2 = 0; 
    $t3 = 0; 
    $ins->bindParam(':1', $id, PDO::PARAM_INT); 
    $ins->bindParam(':2', $t1, PDO::PARAM_STR); 
    $ins->bindParam(':3', $t2, PDO::PARAM_STR); 
    $ins->bindParam(':4', $t3, PDO::PARAM_STR); 
    for ($i = 0; $i < 50; $i++) { 
     $id = rand(1000000, 9999999); 
     $t1 = string_random(240); 
     $t2 = string_random(240); 
     $t3 = string_random(1000); 
     $ins->execute(); 
    } 

如果我們使用bindValue代替bindParam,我們需要在執行每一次之前,做bindValue


對於第二個試驗(不使用bindParam),該代碼是易受SQL注入,與第一試驗。爲了使第二個測試等同於第一個測試,我們需要確保這些值已正確轉義,以便它們可以安全地包含在SQL文本中。

for ($i = 0; $i < 50; $i++) { 
     $id = rand(1000000, 9999999); 
     $t1 = string_random(240); 
     $t2 = string_random(240); 
     $t3 = string_random(1000); 
     $sql = "INSERT INTO `test` (id, t1, dt, t2, t3) VALUES (" 
      . $dbh->quote($id) . ", " 
      . $dbh->quote($t1) . ", " 
      . "NOW(), " 
      . $dbh->quote($t2) . ", " 
      . $dbh->quote($t3) . ")"; 
     $ins = $dbh->prepare($sql); 
     $ins->execute(); 
    } 

請注意,引用函數不僅僅是將引號放在值的周圍。它還「逃避」文本中包含的單引號,以及MySQL將解釋的其他字符。 (在原來的第二個測試中,我們沒有看到圍繞這些值的單引號,所以看起來MySQL似乎已經在數值上下文中評估了這些值,我們預計很多這些行在t1中會有「0」 T2和T3列。

我很希望看到這兩個(修改)測試的時間進行比較。

+0

1.它根本無助於他的設置。 2.它無法解釋這種巨大的差異。 –

+0

這使你的答案成爲一個單純的題目。 –

+0

@YourCommonSense:1)我建議的修改*通過消除其他差異來設置bindParam的性能影響度量。 2)如果其中一個循環插入行,它解釋了「如此巨大的差異」,另一個測試是拋出語法錯誤。您可能認爲OP比較是「bindParam」性能的有效測量。您有權獲得您的意見。你可以自由地降低我的答案。到目前爲止只有一個downvote,所以隨時添加你的。 – spencer7593

2

在代碼中的第3行,你有以下錯誤host=127這就是爲什麼bindParam有太慢基準

$dbh = new PDO('mysql:host=127.0.0.1;port=3306', 'root', 'root'); 

編輯 在您的例子,你能避免連接到MySQL服務器的4倍(2路每次2次)並且不要關閉連接一次。另外,不需要對USE數據庫Web_Amicrom_HQ進行額外查詢,則可以在連接中傳遞該信息。

的完整代碼

$dbh = new PDO('mysql:host=localhost;dbname=Web_Amicrom_HQ', 'root', 'root'); 
    for ($j = 0; $j < 2; $j++) { 
      $start = microtime(true); 
     for ($i = 0; $i < 50; $i++) { 
      $id = rand(1000000, 9999999); 
      $t1 = string_random(240); 
      $t2 = string_random(240); 
      $t3 = string_random(1000); 
      $ins = $dbh->prepare('INSERT INTO `test` (id, t1, dt, t2, t3) VALUES(:1, :2, now(), :3, :4)'); 
      $ins->bindParam(':1', $id, PDO::PARAM_INT); 
      $ins->bindParam(':2', $t1, PDO::PARAM_STR); 
      $ins->bindParam(':3', $t2, PDO::PARAM_STR); 
      $ins->bindParam(':4', $t3, PDO::PARAM_STR); 
      $ins->execute(); 
     } 
     echo "---- with bindParam ----\n"; 
     echo microtime(true) - $start; // time elapsed 
     echo "\n\n"; 
    } 
    for ($j = 0; $j < 2; $j++) { 

     $start = microtime(true); 
     for ($i = 0; $i < 50; $i++) { 
      $id = rand(1000000, 9999999); 
      $t1 = string_random(240); 
      $t2 = string_random(240); 
      $t3 = string_random(1000); 
      $ins = $dbh->prepare("INSERT INTO `test` (id, t1, dt, t2, t3) VALUES($id, $t1, now(), $t2, $t3)"); 
      $ins->execute(); 
     } 
     echo "---- with bindParam ----\n"; 
     echo microtime(true) - $start; // time elapsed 
     echo "\n\n"; 
    } 
$dbh = null; 
+0

這是什麼錯誤? –

+0

在你的例子中你的數據庫連接中有兩個不同的主機值。 127和127.0.0.1,第一個你得到3300秒的響應......對於第二個40秒,你仍然不認爲第一個連接有問題? –

+0

在'bindParam'的例子中,你的運行代碼是host = 127嗎?如果是的話,你可以使它成爲'127.0.0.1'並給我寫基準結果? –