2010-01-25 86 views
9

我在寫一個半簡單的數據庫包裝類,並希望有一個自動運行的提取方法:它應該只在第一次準備每個不同的語句並且只是在連續調用時綁定並執行查詢。PHP PDO:如何重新編寫一個聲明會影響性能

我猜主要問題是:如何重新準備相同的MySql語句工作,PDO會奇蹟般地識別語句(所以我不必)並停止操作?

如果,我打算實現通過生成每個不同的查詢唯一鍵做到這一點,並保持預處理語句在數據庫對象的私人陣列 - 根據其獨特的密鑰。我打算通過以下方式之一獲取數組密鑰(我並不喜歡它們)。在優先順序:

  • 已調用方法時,程序員通過一個額外的,總是相同的參數 - 沿basename(__FILE__, ".php") . __LINE__東西線(此方法只會工作,如果我們的方法是一個循環中調用 - 這是在大多數情況下需要此功能)
  • 讓程序員傳遞一個完全隨機的字符串(很可能是預先生成的)作爲額外參數
  • 使用傳遞的查詢本身來生成密鑰 - 獲取查詢或類似的東西
  • 通過cal實現與第一個項目符號(上面)相同ling debug_backtrace

有沒有類似的經驗?儘管我爲工作的系統確實有值得關注優化(這個數字相當大,並在一週內增長),也許我不擔心什麼,並且在做我正在做的事情時沒有性能優勢?

+0

我認爲把準備好的語句句柄放在一個數組中,用SQL作爲鍵是唯一的方法。我認爲你提出的其他方法沒有任何好處。然而,我很想知道PDO是否自動進行這種優化...... – Inshallah 2010-01-25 13:29:12

+0

但是,如果查詢是一個很長的並且被稱爲數千次,那麼在數組中查找這樣一個鍵將成爲它自己的IMO的瓶頸。 – raveren 2010-01-25 13:43:24

+0

我不知道。我可能完全錯誤。我對PHP數組內部知識不夠了解,從未測試過這個級別的性能。順便說一句,這是一個好主意,在實施之前測試各種方式的性能。 – Inshallah 2010-01-25 13:46:30

回答

1

好的,因爲我一直在對緩存的查詢進行鍵控,而不是簡單地使用查詢字符串本身,所以我做了一個天真的基準測試。下面比較了使用普通的查詢字符串VS首先創建MD5哈希:

$ php -v 
$ PHP 5.3.0-3 with Suhosin-Patch (cli) (built: Aug 26 2009 08:01:52) 
$ ... 
$ php benchmark.php 
$ PHP hashing: 0.19465494155884 [microtime] 
$ MD5 hashing: 0.57781004905701 [microtime] 
$ 799994 

代碼:

<?php 
error_reporting(E_ALL); 

$queries = array("SELECT", 
       "INSERT", 
       "UPDATE", 
       "DELETE", 
       ); 
$query_length = 256; 
$num_queries = 256; 
$iter = 10000; 

for ($i = 0; $i < $num_queries; $i++) { 
    $q = implode('', 
      array_map("chr", 
      array_map("rand", 
         array_fill(0, $query_length, ord("a")), 
         array_fill(0, $query_length, ord("z"))))); 
    $queries[] = $q; 
} 

echo count($queries), "\n"; 

$cache = array(); 
$side_effect1 = 0; 
$t = microtime(true); 
for ($i = 0; $i < $iter; $i++) { 
    foreach ($queries as $q) { 
     if (!isset($cache[$q])) { 
      $cache[$q] = $q; 
     } 
     else { 
      $side_effect1++; 
     } 
    } 
} 
echo microtime(true) - $t, "\n"; 

$cache = array(); 
$side_effect2 = 0; 
$t = microtime(true); 
for ($i = 0; $i < $iter; $i++) { 
    foreach ($queries as $q) { 
     $md5 = md5($q); 
     if (!isset($cache[$md5])) { 
      $cache[$md5] = $q; 
     } 
     else { 
      $side_effect2++; 
     } 
    } 
} 
echo microtime(true) - $t, "\n"; 

echo $side_effect1 + $side_effect2, "\n"; 
+0

感謝您的基準測試,可惜我無法在這裏工作,因爲$ cache從來沒有任何數據,$ q只是「DELETEArray」,但我會盡快進行調查 – dbemerlin 2010-01-25 14:20:42

+0

@dbemerlin,是的,抱歉。它現在應該工作。從emacs複製/粘貼後,我編輯了一下代碼,在這個過程中引入了一些錯誤:-)。 – Inshallah 2010-01-25 14:36:35

+0

我現在基於腳本對這些修改進行了測試:通過連接20次來自$ queries的隨機字符串創建100個隨機字符串。這些隨機字符串的foreach循環$ iter次,每次檢查$ cache中是否存在字符串,如果沒有設置它。對於md5:隨機字符串的foreach會創建散列,然後循環$ iter次並執行相同的操作。結果:在$ iter = 1的情況下,md5散列速度慢0.00008ms,對於$ iter低值(100或更低),沒有真正的性能差異(<0.1s),隨着迭代次數的增加,md5獲得更多優勢。取決於用例。 – dbemerlin 2010-01-25 14:37:00

1

據我所知,PDO不會重複使用已經準備好的語句,因爲它不會自己分析查詢,所以它不知道它是否是相同的查詢。

如果要創建準備查詢的緩存,最簡單的方法是將md5哈希到查詢字符串並生成查找表。

OTOH:您執行多少個查詢(每分鐘)?如果少於幾百,那麼你只會使代碼複雜化,性能增益會很小。

+0

我可能會丟失一些東西,但爲什麼要創建查詢字符串的MD5?爲什麼不使用查詢字符串_itself_? – Inshallah 2010-01-25 13:34:10

+0

,因爲查詢字符串本身可能很長,所以查找速度會變慢。哈希將允許更快的查找(當然,如果查詢足夠短並且查找次數足夠少,那麼它實際上可能會更慢,但在通常情況下哈希搜索速度更快) – dbemerlin 2010-01-25 13:37:08

+0

什麼? MD5ing查詢字符串的性能?這完全是瘋了:-)。我已經看到了Perl中使用的查詢字符串方法的索引,效果很好。你確定你對PHP數組的實現方式有足夠的瞭解以作出這樣的建議嗎? – Inshallah 2010-01-25 13:39:48

4

相信我,我以前和建設準備語句的性能增益是一個高速緩存後做到了這一點非常明顯的 - 看到這個問題:Preparing SQL Statements with PDO

的,這是我後,來到了,隨着緩存預處理語句代碼:

function DB($query) 
{ 
    static $db = null; 
    static $result = array(); 

    if (is_null($db) === true) 
    { 
     $db = new PDO('sqlite:' . $query, null, null, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING)); 
    } 

    else if (is_a($db, 'PDO') === true) 
    { 
     $hash = md5($query); 

     if (empty($result[$hash]) === true) 
     { 
      $result[$hash] = $db->prepare($query); 
     } 

     if (is_a($result[$hash], 'PDOStatement') === true) 
     { 
      if ($result[$hash]->execute(array_slice(func_get_args(), 1)) === true) 
      { 
       if (stripos($query, 'INSERT') === 0) 
       { 
        return $db->lastInsertId(); 
       } 

       else if (stripos($query, 'SELECT') === 0) 
       { 
        return $result[$hash]->fetchAll(PDO::FETCH_ASSOC); 
       } 

       else if ((stripos($query, 'UPDATE') === 0) || (stripos($query, 'DELETE') === 0)) 
       { 
        return $result[$hash]->rowCount(); 
       } 

       else if (stripos($query, 'REPLACE') === 0) 
       { 
       } 

       return true; 
      } 
     } 

     return false; 
    } 
} 

因爲我不需要擔心碰撞在查詢中,我已經結束了使用的md5()代替sha1()

+0

我很想知道爲什麼你決定使用MD5哈希。我已經發布了一個MD5哈希與PHP本地數組的天真基準,並可以找到MD5方法的改進。 – Inshallah 2010-01-25 14:14:20

+0

@Inshallah:使用'md5()'的決定只是讓我在查詢中不會有新行('\ n')。 – 2010-01-25 14:27:17

6

MySQL的(最喜歡的DBMS)將緩存準備的語句的執行計劃,因此,如果用戶A創建一個計劃:

SELECT * FROM some_table WHERE a_col=:v1 AND b_col=:v2 

(其中V1和V2是綁定VARS),然後發送到由被內插的值DBMS,則用戶B發送相同的查詢(但插值有不同的值),DBMS不必重新生成計劃。即它是找到匹配計劃的DBMS--而不是PDO。

但是這意味着數據庫上的每個操作都需要至少2個往返行程(第1個呈現查詢,第2個呈現綁定變量),而不是針對具有字面值的查詢的單個往返行程,引入額外的網絡成本。取消引用(和維護)查詢/計劃緩存也涉及很小的成本。

關鍵問題是這個成本是否大於首先生成計劃的成本。雖然(以我的經驗),使用Oracle準備好的語句似乎確實具有性能優勢,但我不相信MySQL也是如此 - 但是,很多將取決於數據庫的結構,查詢的複雜性(或者更具體地說,優化器可以找到多少個不同的選項來解析查詢)。 (提示:您可能希望將慢速查詢閾值設置爲0,並編寫一些代碼將文字值轉換回寫入日誌的查詢的匿名錶示)。

+0

-1如[準備好的語句和存儲程序緩存](http://dev.mysql.com/doc/en/statement-caching.html)中所述:「*服務器維護準備語句和存儲程序的緩存一個會話緩存的語句不能被其他會話訪問,當一個會話結束時,服務器會放棄爲它緩存的所有語句*「也許會產生混淆,因爲準備語句在變量擴展之後被緩存在正常如[查詢緩存如何操作](http://dev.mysql.com/doc/en/query-cache-operation.html)中所述。 – eggyal 2014-03-10 08:18:56

0

使用MD5哈希值作爲重點,你可能最終得到這一結果在相同的兩個查詢MD5哈希。概率不高,但可能發生。不要這樣做。像MD5這樣的有損散列算法只是用來判斷兩個對象是否具有高確定性,但不是一種安全的識別方法。