У меня есть очень большая таблица, которую я хочу обрабатывать построчно в PHP.
Вот что я попробовал:
PDO::fetchAll()
Загружает все строки в памяти немедленно: ошибка нехватки памяти.
Пагинация с PDO::fetchAll()
а также LIMIT X, 1000
(то есть разделение большого запроса на более мелкие)
Я был удивлен, увидев, что использование памяти постоянно росло до недостаточно памяти (по крайней мере, до 1 Гб). Почему память не освобождается между запросами?
У меня нет глобальных переменных, нет свойств класса / объекта, нумерация страниц заключена в методе, поэтому локальные переменные (строки, извлеченные из базы данных) должны быть собраны сборщиком мусора после обработки текущей страницы… Я также не храню строки в памяти, я записываю на диск.
Также плохая вещь с этой техникой состоит в том, что она приводит к нескольким запросам вместо одного, которые медленны на очень большой таблице.
PDO::fetchAll()
с буферизованные запросы
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
Наконец, использование памяти остается очень низким (~ 13 МБ), но процесс очень медленный, так как каждая строка означает сетевой доступ к серверу MySQL.
Итак, мои вопросы:
Или вы видите другое лучшее решение?
Возможно, я не понимаю ваш вопрос … Но я создал простой скрипт, который перебирает все значения из таблицы с помощью ~ 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 Гб.
Других решений пока нет …