оптимизация скрипта (Fatal error: Allowed memory)

270
10 декабря 2016, 10:44

Привет! Возникла проблема - при чтении из большой таблицы базы данных скрипт выдаёт ошибку:

Allow memory...

Код следующий:

$query = mysql_query('SELECT * FROM `table`;');
while ($res = mysql_fetch_array($query))
{
    echo $res['id'] . ',';
    unset($res);
}

Можно ли как-то оптимизировать, чтобы не получать данную ошибку?

Answer 1

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

При использовании функции mysql_query, она буферизирует результат, вы работаете с курсором данных, которые обработаны и сохранены в память скрипта PHP, естественно при большом обьеме данных, обычного размера памяти выделяемого под скрипт не хватит. Это называется буферизированный запрос.

На помощь приходит функция mysql_unbuffered_query, она не сохраняет результат выполнения в память, а выполняет перемещение курсора в базе данных, благодаря чему память скрипта не засоряется. Это называется небуферизированный запрос.

Тоесть в случае mysql_query вы работате с курсором из памяти PHP, а во втором случае с курсором базы данных. Меняйте mysql_query на mysql_unbuffered_query и все будет отлично.

Хочу заметить, что расширение mysql устарело и его использование небезопасно, как минимум из-за отсутствия функций привязки параметров, и отсуствия поддержки в PHP 5.5 и выше, воспользуйтесь библиотекой mysqli или PDO, последняя предоставляет более удобный обьектно-ориентированный доступ к данным.

У небуферизированных запросов есть преимущества и недостатки:

Преимущества:

  1. Результат можно начинать читать раньше, сокращается время ожидания;
  2. Результат не занимает место в оперативной памяти.

Недостатки:

  1. Невозможно узнать, сколько строк получено;
  2. Невозможно передвигаться к определенному результату, то есть можно читать данные только с начала и по порядку;
  3. Нельзя выполнять других запросов, пока не закрыт этот результат.

Небуферизированный запрос на PDO выглядит вот так:

<?php
$pdo = new PDO("mysql:host=localhost;dbname=world", 'my_user', 'my_pass');
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
$uresult = $pdo->query("SELECT Name FROM City");
if ($uresult) {
   while ($row = $uresult->fetch(PDO::FETCH_ASSOC)) {
       echo $row['Name'] . PHP_EOL;
   }
}
?>

На официальном сайте PHP есть информация по буфферизированным и небуферизированным запросом на английском языке.

P.S Если вам нужен только ID выбирайте в запросе только id, а не все поля из таблицы

Answer 2

Чтобы оптимизировать работу с памятью при задаче обхода большой таблицы, выбирайте не всю таблицу, а только колонку с первичным ключом из неё:

/*Обход большой таблицы с экономией памяти*/
$query = mysql_query('SELECT id FROM `table`;');
while ($id = mysql_fetch_array($query)[0])
{
    $row = mysql_fetch_array( mysql_query('SELECT * FROM `table` where id = '.$id.';') );
    /* Делаем что-то с полными данными ряда таблицы $row */
    ...
}

Внутри цикла, если того требует задача - выберите полные данные по ряду, зная id. В таком случае каждый шаг цикла, память, захваченная переменной $row - отправится в garbage collector, т.е. освободится при необходимости.

Кстати это догма при написании скриптов портирования на PHP. Догма эта правда относится к построителям запросов - которые не различают query и fetch в общем случае для упрощения работы с БД.

Замечу, что драйвер mysql и уязвим, о чём написано на каждой странице php.net, ему посвящённой. Необходимо использовать PDO , Doctrine DBAL , или на худой конец mysqli.

READ ALSO
Прикрепить меню к верху страницы [дубликат]

Прикрепить меню к верху страницы [дубликат]

На данный вопрос уже ответили:

304
Редактирование текста как в google docs

Редактирование текста как в google docs

Как можно сделать ввод текста с возможностью форматирования (жирный/курсив/цветной итд

354
Как дать рамку картинкой css?

Как дать рамку картинкой css?

Собственно есть рамка в фотошопе, но как сверстать - не знаюПолучается криво

297
Как добавить стили к выводу таблицы?

Как добавить стили к выводу таблицы?

Есть код для вывода таблицы из Oracle:

263