понедельник, 10 сентября 2012 г.

PHP расширение mysqli - Асинхронные запросы



Асинхронный, неблокирующий ввод-вывод это старая идея. Вот и в PHP расширении mysqli присутствует возможность асинхронных запросов. Эта возможность доступна только при использовании библиотеки mysqlnd (в противовес libmysql).

В обычной схеме, после отправки запроса к БД PHP-скрипт заблокирован до получения ответа (в самом распространённом варианте: до получения всех записей соответствующих запросу). При использовании асинхронности скрипт продолжает работу сразу же после отправки запроса, а затем уже можно производить опрос mysqli-объектов с целью получения ответов.

По-моему применение асинхронных запросов специфично в условиях больших проектов, где используется распределение БД или, возможно, в цикличных системах, где необходима постоянная «отзывчивость» программы. В реальной жизни с такими условиями я ещё не сталкивался, но функциональность есть, и я её рассмотрю.

MySQL протокол клиент-сервер не имеет встроенной поддержки асинхронности, поэтому для её реализации в PHP необходимо отдельное соединение (объект mysqli) на каждый запрос.

Для отправка асинхронного запроса в метод mysqli::query() добавляется второй параметр «MYSQLI_ASYNC»:
$mysqli = new mysqli('host', 'user', 'pass', 'database');
$mysqli->query('...', MYSQLI_ASYNC);
При опросе соединения на предмет получения результата предлагается использовать статический метод mysqli::poll(). Но в PHP 5.3.3 (той, что у меня сейчас установлена) этот метод почему-то недоступен, поэтому я использую процедурный вариант, т.е. mysqli_poll():
mysqli_poll($read, $write, $error, 1);
Добавим проверку ошибок, вывод результатов и получим рабочий пример:
$mysqli = new mysqli('host', 'user', 'pass', 'database');
$mysqli->query('SELECT * FROM `test`', MYSQLI_ASYNC);
 
$all_links = array($mysqli);
$processed = 0;
 
do
{
    $read = $write = $error = array();
 
    foreach ($all_links as $link)
    {
        $read[] = $write[] = $error[] = $link;
    }
 
    if (!mysqli_poll($read, $write, $error, 1))
    {
        if (count($error))
            foreach ($error as $link)
            {
                echo $link->error . '<br />';
                $processed++;
        }
    }
 
    foreach ($read as $link)
    {
        if ($result = $link->reap_async_query())
        {
            if (is_object($result)) // for SELECT
            {
                print_r($result->fetch_row());
                $result->free();
 
            } else { // for INSERT / UPDATE / DELETE
 
                print_r($link);
            }
 
            $processed++;
        }
    }
}
while ($processed < count($all_links));
 
$mysqli->close();
Описанная выше функциональность плохо документирована, поэтому следующие ссылки могут пригодиться:

  1. PHP: parallel, background, asynchronous fetch
  2. PHP: How mysqlnd async queries help you with sharding!

Комментариев нет:

Отправить комментарий