W3docs

reap_async_query

Узнайте, как mysqli_reap_async_query() получает результат асинхронного запроса MySQLi в PHP, с примером параллельных запросов через mysqli_poll.

Введение

mysqli_reap_async_query() получает результат запроса, запущенного асинхронно с помощью расширения MySQLi. Асинхронный запрос — это запрос, который вы отправляете, не ожидая завершения работы сервера: PHP-скрипт продолжает выполняться, а результат вы забираете позже, когда сервер сообщит о готовности.

Это недостающее звено асинхронного рабочего процесса MySQLi. Сама по себе функция mysqli_reap_async_query() бесполезна: она имеет смысл только как завершающий шаг трёхэтапного паттерна, состоящего из mysqli_query(..., MYSQLI_ASYNC) (или mysqli_send_query()), mysqli_poll() и mysqli_reap_async_query(). На этой странице объясняется, как эти части работают вместе, приведён полный рабочий пример и перечислены распространённые ошибки.

Требование: асинхронные запросы работают только с драйвером mysqlnd (стандартный нативный драйвер в современных сборках PHP). Они недоступны, если MySQLi скомпилирован с устаревшим libmysqlclient.

Жизненный цикл асинхронного запроса

Один асинхронный запрос проходит три стадии:

  1. Отправка — запустите запрос с флагом MYSQLI_ASYNC. mysqli_query($conn, $sql, MYSQLI_RESULT, MYSQLI_ASYNC) (или сокращённый вариант mysqli_send_query()) возвращает управление немедленно, не ожидая результатов.
  2. Опрос — вызовите mysqli_poll(), чтобы дождаться готовности одного или нескольких соединений. Именно здесь происходит блокировка (с управляемым таймаутом), а не на самом запросе.
  3. Получение — как только опрос сообщает о готовности соединения, вызовите mysqli_reap_async_query($conn), чтобы получить mysqli_result для этого соединения.

Ключевой момент — шаг опроса. Вы можете передать несколько соединений в один вызов mysqli_poll(), и их запросы будут выполняться на сервере одновременно. Суммарное время ожидания составит примерно время самого медленного запроса, а не сумму всех.

mysqli_poll(): шаг, который нельзя пропустить

Распространённая ошибка — вызывать mysqli_reap_async_query() сразу после отправки запроса. Если результат ещё не готов, функция вернёт false и установит ошибку — она не ждёт. Ожидание обеспечивает mysqli_poll().

mysqli_poll() принимает массивы соединений, передаваемые по ссылке, и таймаут:

mysqli_poll($read, $error, $reject, $sec, $usec);
  • $read — массив соединений для отслеживания. После вызова в нём остаются только соединения с готовым результатом.
  • $error / $reject — принимают соединения с протокольными ошибками или отклонёнными запросами.
  • $sec / $usec — время ожидания в секундах и микросекундах.

Возвращает количество готовых соединений (0 при таймауте, false при ошибке).

Полный пример: выполнение двух запросов параллельно

Пример ниже открывает два соединения, запускает асинхронный запрос на каждом, ожидает результатов и получает их. Замените учётные данные и SQL на собственные.

<?php
// One connection per concurrent query.
$conn1 = mysqli_connect("localhost", "user", "password", "shop");
$conn2 = mysqli_connect("localhost", "user", "password", "shop");

// 1. Send both queries asynchronously — neither call blocks.
mysqli_query($conn1, "SELECT COUNT(*) AS n FROM orders", MYSQLI_STORE_RESULT, MYSQLI_ASYNC);
mysqli_query($conn2, "SELECT COUNT(*) AS n FROM customers", MYSQLI_STORE_RESULT, MYSQLI_ASYNC);

$links = [$conn1, $conn2];
$pending = count($links);

// 2. Poll until every connection has reported back.
while ($pending > 0) {
    $read = $error = $reject = $links;

    // Wait up to 1 second for any connection to become ready.
    if (!mysqli_poll($read, $error, $reject, 1)) {
        continue; // timeout — nothing ready yet, loop again
    }

    // 3. Reap each ready connection.
    foreach ($read as $link) {
        $result = mysqli_reap_async_query($link);
        if ($result) {
            $row = mysqli_fetch_assoc($result);
            echo "Count: " . $row["n"] . "\n";
            mysqli_free_result($result);
        } else {
            echo "Query error: " . mysqli_error($link) . "\n";
        }
        $pending--;
    }
}
?>

Каждое соединение может выполнять только один асинхронный запрос одновременно — именно поэтому в примере используется отдельное соединение для каждого запроса. Повторно использовать соединение для нового асинхронного запроса можно только после получения результата предыдущего.

Пошаговое резюме

  1. Откройте по одному соединению на каждый параллельный запрос (mysqli_connect()).
  2. Запустите каждый запрос с флагом MYSQLI_ASYNC, чтобы вызов возвращал управление немедленно.
  3. Соберите соединения в массив и передайте его в mysqli_poll() с таймаутом.
  4. Для каждого соединения, которое mysqli_poll() отметит как готовое, вызовите mysqli_reap_async_query().
  5. Обработайте возвращённый mysqli_result, затем освободите его с помощью mysqli_free_result().

Возвращаемые значения и обработка ошибок

mysqli_reap_async_query() возвращает:

  • Объект mysqli_result для запросов, возвращающих набор данных (например, SELECT).
  • true для запросов без возвращаемых строк (INSERT, UPDATE, DELETE) при успешном выполнении.
  • false при ошибке или при вызове до готовности результата — в этом случае проверьте mysqli_error().

Всегда вызывайте функцию получения результата только после того, как mysqli_poll() подтвердит готовность соединения. Получение результата от неготового соединения — самая частая причина загадочного возврата false.

Сценарии использования неблокирующих запросов MySQLi

Неблокирующие запросы MySQLi полезны PHP-разработчикам, которым нужно выполнять несколько запросов параллельно или запускать долгие запросы, не блокируя выполнение остального кода. Вот практические сценарии использования:

1. Параллельное выполнение запросов

Разработчики могут использовать неблокирующие запросы MySQLi для выполнения нескольких независимых запросов параллельно. Отправляя каждый запрос через mysqli_send_query() и перемежая с другой логикой, приложения сокращают общее время ожидания при получении данных из нескольких таблиц или сервисов.

2. Долгие запросы

Долгие запросы можно запускать асинхронно, чтобы PHP-скрипт мог выполнять другие задачи — логирование, обновление UI или обработку ввода пользователя — пока база данных завершает операцию.

3. Приложения реального времени

Приложения, требующие частого опроса данных или обновлений в реальном времени, могут запускать запросы без блокировки основного потока выполнения. Это особенно полезно для CLI-инструментов мониторинга или лёгких веб-эндпоинтов, которым важно быстро возвращать ответ.

4. Асинхронная обработка данных

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

Преимущества неблокирующих запросов MySQLi

Неблокирующие запросы MySQLi предоставляют PHP-разработчикам ряд преимуществ:

1. Повышенная производительность

Асинхронное выполнение запросов не блокирует другой код, что ускоряет работу приложения. Это особенно выгодно для приложений, агрегирующих данные из нескольких источников или обрабатывающих высококонкурентные запросы.

2. Лучшее использование ресурсов

Неблокирующее выполнение позволяет PHP-процессу оставаться отзывчивым в ожидании операций с базой данных, сокращая простой и улучшая утилизацию ресурсов сервера.

3. Упрощённое управление фоновыми задачами

Разработчики могут объединять несколько операций с базой данных без вложенных колбэков или сложных конечных автоматов, что делает код легче для чтения и поддержки в стандартных процедурных PHP-скриптах.

Заключение

mysqli_reap_async_query() — завершающий шаг асинхронного рабочего процесса MySQLi: вы запускаете запрос с флагом MYSQLI_ASYNC, ожидаете его с помощью mysqli_poll(), а затем получаете результат. Совместно эти функции позволяют PHP-скрипту выполнять несколько запросов к MySQL параллельно и собирать результаты по мере их готовности, а не ждать каждый по очереди. Наибольший выигрыш достигается при наличии нескольких независимых запросов — суммарное время ожидания снижается примерно до времени самого медленного запроса, а не суммы всех. Помните два правила надёжной работы: один асинхронный запрос на соединение, и никогда не получайте результат до того, как mysqli_poll() подтвердит готовность соединения.

Связанные темы

  • mysqli_poll — ожидание готовности одного или нескольких асинхронных соединений.
  • mysqli_multi_query — выполнение нескольких операторов за один вызов.
  • mysqli_query — стандартная (синхронная) функция запроса.
  • mysqli_connect — открытие соединений для выполнения запросов.
  • PHP MySQLi — обзор расширения MySQLi.

Практика

Практика
Что верно относительно выполнения MySQL-запроса в PHP с помощью метода reap async query?
Что верно относительно выполнения MySQL-запроса в PHP с помощью метода reap async query?
Was this page helpful?