Функция PHP mysqli_poll()
Функция PHP mysqli_poll(): синтаксис, параметры, возвращаемые значения и пример опроса асинхронных соединений MySQL.
Функция mysqli_poll() позволяет ожидать результатов сразу нескольких соединений MySQL и определять, какие из них завершили выполнение асинхронного запроса. Это связующее звено между запуском неблокирующих запросов и получением их результатов без блокировки всего скрипта на самом медленном из них.
Что делает mysqli_poll()
При обычном выполнении запроса PHP останавливается и ждёт ответа от MySQL. При использовании асинхронных запросов (запущенных с флагом MYSQLI_ASYNC) запрос отправляется, но скрипт продолжает работу. Возникает вопрос: как узнать, когда результат готов к чтению? На него отвечает mysqli_poll().
Вы передаёте ей список соединений, и она блокируется до тех пор, пока хотя бы одно из них не будет готово вернуть результат (или пока не истечёт таймаут). Затем функция сообщает, какие соединения готовы, и вы получаете их результаты с помощью reap_async_query().
Это особенно важно, когда нужно выполнять несколько независимых запросов параллельно — например, запрашивать две разные базы данных или одновременно запускать несколько медленных отчётов. Вместо того чтобы выполнять каждый запрос последовательно, вы запускаете их все и позволяете им работать одновременно.
Важно: mysqli_poll() работает только с драйвером mysqlnd. С более старым драйвером libmysql она недоступна. Функция является только процедурной — объектно-ориентированного метода $mysqli->poll() не существует; всегда вызывайте её как mysqli_poll(...).
Синтаксис
mysqli_poll(
array &$read,
array &$error,
array &$reject,
int $seconds,
int $microseconds = 0
): int|falseПараметры
| Параметр | Описание |
|---|---|
$read | Список соединений для проверки наличия результатов. Передаётся по ссылке — при возврате перезаписывается и содержит только соединения с готовыми результатами. |
$error | Перезаписывается и содержит соединения, в которых произошла ошибка. |
$reject | Перезаписывается и содержит отклонённые соединения (без ожидающего асинхронного запроса). |
$seconds | Максимальное количество секунд ожидания. |
$microseconds | Дополнительные микросекунды ожидания (необязательно, по умолчанию 0). |
Возвращаемое значение
Возвращает количество готовых соединений в случае успеха или false при ошибке. Возврат 0 означает, что таймаут истёк до того, как какое-либо соединение стало готовым — это не является ошибкой.
Как использовать функцию mysqli_poll()
Шаблон всегда состоит из трёх шагов:
- Открыть по одному соединению на каждый запрос, который нужно выполнить параллельно.
- Запустить каждый запрос с флагом
MYSQLI_ASYNC, чтобы он не блокировал выполнение. - В цикле вызывать
mysqli_poll()для ожидания следующего завершившегося соединения и получения его результата.
<?php
// Open one connection per parallel query.
$conn1 = new mysqli("localhost", "user", "pass", "shop");
$conn2 = new mysqli("localhost", "user", "pass", "shop");
foreach ([$conn1, $conn2] as $c) {
if ($c->connect_errno) {
exit("Connect failed: " . $c->connect_error);
}
}
// Fire both queries asynchronously — neither call blocks.
$conn1->query("SELECT SLEEP(1), 'orders done' AS msg", MYSQLI_ASYNC);
$conn2->query("SELECT SLEEP(2), 'reports done' AS msg", MYSQLI_ASYNC);
$pending = [$conn1, $conn2];
while (!empty($pending)) {
// Copies that mysqli_poll() will rewrite by reference.
$read = $pending;
$error = $pending;
$reject = $pending;
// Block up to 5 seconds for at least one connection to become ready.
if (mysqli_poll($read, $error, $reject, 5) === false) {
echo "Poll failed.\n";
break;
}
// $read now holds only the connections with a result waiting.
foreach ($read as $conn) {
if ($result = $conn->reap_async_query()) {
$row = $result->fetch_assoc();
echo $row['msg'] . "\n";
$result->free();
} else {
echo "Query error: " . $conn->error . "\n";
}
// Remove this connection from the pending list.
$pending = array_filter($pending, fn($c) => $c !== $conn);
}
}
$conn1->close();
$conn2->close();
?>Несмотря на то что первый запрос ждёт 1 секунду, а второй — 2 секунды, весь скрипт завершается примерно за 2 секунды, а не за 3 — оба запроса выполнялись одновременно. Поскольку запрос на 1 секунду завершается первым, вывод следующий:
orders done
reports doneЧто делает код, шаг за шагом
- Открываются два соединения, потому что каждое соединение может выполнять только один асинхронный запрос одновременно.
query(..., MYSQLI_ASYNC)отправляет каждый запрос и немедленно возвращает управление, не ожидая ответа.- Перед каждым вызовом
mysqli_poll()мы копируем$pendingв$read,$errorи$reject, потому что функция перезаписывает эти массивы по ссылке.$readвозвращается с готовыми соединениями,$error— с неудачными,$reject— с теми, у которых нет ожидающего запроса. - Для каждого готового соединения вызывается
reap_async_query()для получения набора результатов, после чего соединение удаляется из$pending, чтобы не опрашивать его снова. - Цикл завершается, когда все запросы получены.
Примечание: Для сложных асинхронных нагрузок рассмотрите библиотеки цикла событий, такие как ReactPHP или Swoole, которые предоставляют более надёжную архитектуру, чем ручное написание цикла опроса.
Связанные функции
reap_async_query()— получить результат соединения, котороеmysqli_poll()отметила как готовое.query()— выполнить запрос (добавьтеMYSQLI_ASYNC, чтобы сделать его неблокирующим).multi_query()— выполнить несколько инструкций за один вызов для одного соединения.connect_errno— проверить, завершилась ли попытка подключения ошибкой.
Заключение
mysqli_poll() — это элемент, который делает параллельные запросы MySQL практичными в PHP: она ожидает результатов от множества соединений одновременно и сообщает, какие из них готовы к чтению. Используйте её вместе с асинхронными запросами MYSQLI_ASYNC и reap_async_query(), помните, что требуется драйвер mysqlnd, и вы сможете сократить общее время выполнения нескольких независимых запросов примерно до времени самого медленного из них.