問題
通常一個web應用的效能瓶頸在資料庫。因為,通常情況下php中mysql查詢是串列的。也就是說,如果指定兩個sql語句時,第二個sql語句會等到第一個sql語句執行完畢再去執行。這時候,如果執行2個sql語句,每個執行時間為50ms,全部執行完畢可能需要100ms。既然,主要原因是sql的串列執行導致。那我們是不是可以改變執行方式來提升效能呢?答案是,可以的。我們可以透過非同步執行的方式來提高效能。
非同步
如果透過非同步的方式去執行,可能效能會有很大提升。如果是採用非同步的方式,兩個sql語句會並發執行,可能就需要60ms就可以執行完畢。
實作
mysqli + mysqlnd。 php官方實作的mysqlnd中提供了非同步查詢的方法。分別是:
mysqlnd_async_query 發送查詢請求
mysqlnd_reap_async_query 取得查詢結果
這樣就可以不必每次發送完查詢請求後,一直阻塞等待查詢結果了。
實作程式碼如下:
<!--?php $host = '127.0.0.1'; $user = 'root'; $password = ''; $database = 'test'; /** * 期望得到额结果 * array( * 1 =--> int, * 2 => int, * 3 => int * ) */ $result = array(1=>0, 2=>0, 3=>0); //异步方式[并发请求] $time_start = microtime(true); $links = array(); foreach ($result as $key=>$value) { $obj = new mysqli($host, $user, $password, $database); $links[spl_object_hash($obj)] = array('value'=>$key, 'link'=>$obj); } $done = 0; $total = count($links); foreach ($links as $value) { $value['link']->query("SELECT COUNT(*) AS `total` FROM `demo` WHERE `value`={$value['value']}", MYSQLI_ASYNC); } do { $tmp = array(); foreach ($links as $value) { $tmp[] = $value['link']; } $read = $errors = $reject = $tmp; $re = mysqli_poll($read, $errors, $reject, 1); if (false === $re) { die('mysqli_poll failed'); } elseif ($re < 1) { continue; } foreach ($read as $link) { $sql_result = $link->reap_async_query(); if (is_object($sql_result)) { $sql_result_array = $sql_result->fetch_array(MYSQLI_ASSOC);//只有一行 $sql_result->free(); $hash = spl_object_hash($link); $key_in_result = $links[$hash]['value']; $result[$key_in_result] = $sql_result_array['total']; } else { echo $link->error, "\n"; } $done++; } foreach ($errors as $link) { echo $link->error, "1\n"; $done++; } foreach ($reject as $link) { printf("server is busy, client was rejected.\n", $link->connect_error, $link->error); //这个地方别再$done++了。 } } while ($done<$total); var_dump($result); echo "ASYNC_QUERY_TIME:", microtime(true)-$time_start, "\n"; $link = end($links); $link = $link['link']; echo "\n";
結語
mysql資料庫對於每個查詢請求都是單獨啟動一個執行緒進行處理。如果mysql伺服器啟動執行緒過多,必然會造成執行緒切換造成系統負載過高。如果在mysql資料庫負載不高的情況下,使用非同步查詢還是不錯的選擇。