PHP mysqli_use_result() — Небуферизованные наборы результатов
Узнайте, как mysqli_use_result() инициирует небуферизованный набор результатов MySQL, чем отличается от mysqli_store_result() и когда его применять.
Когда вы выполняете SELECT в PHP с помощью расширения mysqli, MySQL должен передать совпадающие строки вашему скрипту. Есть два способа их получить: буферизованный (скопировать весь результат в память PHP сразу) и небуферизованный (оставить строки на сервере MySQL и забирать их по одной). Функция mysqli_use_result() инициирует небуферизованный режим.
В этом руководстве объясняется, что делает mysqli_use_result(), чем он отличается от стандартного буферизованного режима, какие правила нужно соблюдать и когда его использование действительно оправдано.
Что делает mysqli_use_result()
mysqli_use_result() инициирует получение набора результатов, созданного запросом, без копирования строк в память PHP. Вместо того чтобы загружать все строки заранее, сервер сохраняет их, а ваш скрипт забирает их по одной в цикле.
Сравните это со стандартным поведением. Когда вы вызываете mysqli_query(), mysqli внутренне вызывает mysqli_store_result() за вас — он загружает весь набор результатов в память клиента до того, как ваш код увидит хоть одну строку. Для запроса, возвращающего десять миллионов строк, это может означать сотни мегабайт памяти PHP. mysqli_use_result() позволяет избежать этих затрат: потребление памяти остаётся примерно постоянным независимо от количества строк.
Синтаксис
mysqli_use_result(mysqli $mysql): mysqli_result|falseФункция принимает объект соединения, возвращённый mysqli_connect(), и возвращает объект mysqli_result в случае успеха или false при ошибке. В объектно-ориентированном стиле эквивалентным методом является $mysqli->use_result().
Буферизованный vs. небуферизованный режим: почему это важно
Буферизованный (store_result) | Небуферизованный (use_result) | |
|---|---|---|
| Память на стороне PHP | Хранит весь набор результатов | Примерно одна строка за раз |
mysqli_num_rows() | Доступно сразу | Только после получения всех строк |
mysqli_data_seek() | Поддерживается | Не поддерживается |
| Соединение во время чтения | Свободно для новых запросов | Заблокировано до завершения |
| Лучше подходит для | Небольших/средних наборов результатов | Очень больших наборов результатов |
Ключевой компромисс: небуферизованный режим экономит память, но занимает соединение. Вы не можете выполнить другой запрос на том же соединении, пока не получите все строки (или не освободите результат). Буферизованный режим — противоположность: больше памяти, но соединение освобождается сразу после возврата запроса.
Как использовать mysqli_use_result()
1. Подключитесь к серверу MySQL
Сначала откройте соединение с помощью mysqli_connect():
<?php
$host = 'localhost';
$user = 'username';
$password = 'password';
$database = 'mydatabase';
$connection = mysqli_connect($host, $user, $password, $database);
if (!$connection) {
die('Connection failed: ' . mysqli_connect_error());
}2. Выполните запрос и начните небуферизованное получение
Для небуферизованного режима нужно выполнить запрос без указания mysqli сохранять результат. Используйте флаг MYSQLI_USE_RESULT с mysqli_query() (или вызовите mysqli_real_query()), а затем вызовите mysqli_use_result():
$sql = "SELECT id, name FROM users";
// MYSQLI_USE_RESULT tells mysqli NOT to buffer the rows.
mysqli_real_query($connection, $sql);
$result = mysqli_use_result($connection);
if ($result) {
while ($row = mysqli_fetch_assoc($result)) {
// Process one row at a time — only this row lives in PHP memory.
print_r($row);
}
mysqli_free_result($result);
}Каждый вызов mysqli_fetch_assoc() получает следующую строку прямо из сети. Аналогично можно использовать mysqli_fetch_row(), mysqli_fetch_array() или mysqli_fetch_all().
3. Всегда освобождайте результат
Вызов mysqli_free_result() здесь важнее, чем в буферизованном режиме: до тех пор, пока результат не освобождён (или не прочитан полностью), соединение остаётся заблокированным и недоступным для других запросов.
Правила и подводные камни
- Прочитайте все строки перед следующим запросом. Пока открыт небуферизованный результат, соединение занято. Попытка выполнить другой запрос до завершения цикла вызовет ошибку "Commands out of sync". Завершите цикл или сначала вызовите
mysqli_free_result(). - Нет счётчика строк заранее.
mysqli_num_rows()возвращает правильное число только после получения всех строк, потому что сервер ещё не сообщил клиенту их количество. - Нет произвольного доступа.
mysqli_data_seek()не работает с небуферизованными результатами — можно двигаться только вперёд. - Один результат на соединение. В один момент времени можно держать открытым только один небуферизованный результат на соединение.
Когда стоит использовать?
Используйте mysqli_use_result(), когда запрос возвращает значительно больше данных, чем вы хотите держать в памяти: при экспорте большой таблицы в CSV, потоковой передаче отчёта или построчной передаче большого результата в другой процесс. В таких случаях постоянный расход памяти — реальное преимущество.
Для обычных запросов, возвращающих несколько строк или даже несколько тысяч строк, придерживайтесь стандартного буферизованного режима через mysqli_query(). Удобство mysqli_num_rows(), произвольного перемещения и свободного соединения почти всегда перевешивает скромную экономию памяти.
Заключение
mysqli_use_result() запускает небуферизованный набор результатов MySQL, получая строки по одной, чтобы расход памяти оставался низким даже для огромных наборов данных. Цена — соединение блокируется до завершения чтения, недоступны mysqli_num_rows() и mysqli_data_seek(), а результат нужно своевременно освобождать. Используйте его для действительно больших наборов данных; для обычных запросов предпочитайте буферизованный режим по умолчанию.