Как правильно разбить большой запрос на страницы с помощью PDO, чтобы избежать «нехватки памяти» ошибки?

У меня есть очень большая таблица, которую я хочу обрабатывать построчно в PHP.

Вот что я попробовал:

  • PDO::fetchAll()

    Загружает все строки в памяти немедленно: ошибка нехватки памяти.

  • Пагинация с PDO::fetchAll() а также LIMIT X, 1000 (то есть разделение большого запроса на более мелкие)

    Я был удивлен, увидев, что использование памяти постоянно росло до недостаточно памяти (по крайней мере, до 1 Гб). Почему память не освобождается между запросами?

    У меня нет глобальных переменных, нет свойств класса / объекта, нумерация страниц заключена в методе, поэтому локальные переменные (строки, извлеченные из базы данных) должны быть собраны сборщиком мусора после обработки текущей страницы… Я также не храню строки в памяти, я записываю на диск.

    Также плохая вещь с этой техникой состоит в том, что она приводит к нескольким запросам вместо одного, которые медленны на очень большой таблице.

  • PDO::fetchAll() с буферизованные запросы

    $pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
    

    Наконец, использование памяти остается очень низким (~ 13 МБ), но процесс очень медленный, так как каждая строка означает сетевой доступ к серверу MySQL.

Итак, мои вопросы:

  • почему метод нумерации страниц вырастает из памяти? есть ли решение для этого?
  • Есть ли способ использовать буферизованные запросы в пакетном режиме? (например, буфер 1000 строк в памяти, чтобы избежать сетевых обходов для каждой строки)

Или вы видите другое лучшее решение?

1

Решение

Возможно, я не понимаю ваш вопрос … Но я создал простой скрипт, который перебирает все значения из таблицы с помощью ~ 5,436,226 строки (и 19 столбцы), и сохраните его в файл. Я использовал PostgeSQL вместо MySQL (но я не думаю, что это проблема, вы должны изменить только раздел LIMIT).

<?
ini_set('memory_limit', '100M');

$pdo  = new PDO('pgsql:host=localhost;port=5432;dbname=test', 'postgres', 'postgres');
$page = 0;
while ($pdo) {
echo ($page++).PHP_EOL;
$stmt = $pdo->prepare('SELECT * FROM table ORDER BY id LIMIT 100 OFFSET '.($page*100));
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
file_put_contents('/var/www/test/tmp/out.txt', json_encode($rows), FILE_APPEND);
}

Размер выходного файла составляет ~ 1 Гб.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]