문제
일반적으로 웹 애플리케이션의 성능 병목 현상은 데이터베이스입니다. 왜냐하면 일반적으로 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 데이터베이스 로드가 높지 않은 경우에도 비동기 쿼리를 사용하는 것이 좋습니다.