Обработка больших наборов данных с помощью PHP / Drupal - PullRequest
2 голосов
/ 19 марта 2010

У меня есть страница отчета, которая имеет дело с ~ 700k записей из таблицы базы данных. Я могу отобразить это на веб-странице, используя подкачку, чтобы разбить результаты. Тем не менее, мой экспорт в PDF / CSV-функции основан на одновременной обработке всего набора данных, и я достигаю лимита памяти в 256 МБ около 250 тыс. Строк.

Мне неудобно увеличивать лимит памяти, и у меня нет возможности использовать сохранение MySQL в outfile, чтобы просто обслуживать предварительно сгенерированный CSV. Тем не менее, я не вижу способа обслуживания больших наборов данных с помощью Drupal, используя что-то вроде:

$form = array();
$table_headers = array();
$table_rows = array();
$data = db_query("a query to get the whole dataset");
while ($row = db_fetch_object($data)) {
    $table_rows[] = $row->some attribute;
}

$form['report'] = array('#value' => theme('table', $table_headers, $table_rows);
return $form;

Есть ли способ обойти то, что по существу добавляется к гигантскому массиву массивов? На данный момент я не понимаю, как я могу предложить какие-либо значимые страницы отчетов с Drupal из-за этого.

Спасибо

Ответы [ 7 ]

4 голосов
/ 19 марта 2010

При таком большом наборе данных я бы использовал Datchal Batch API, который позволяет разбивать трудоемкие операции на партии. Это также лучше для пользователей, потому что это даст им индикатор выполнения с некоторым указанием того, сколько времени займет операция.

Запустите пакетную операцию, открыв временный файл, а затем добавьте новые записи к нему в каждом новом пакете до завершения. Последняя страница может выполнить окончательную обработку, чтобы доставить данные в виде cvs или преобразовать в PDF. Возможно, вы также захотите добавить некоторые слова для очистки.

http://api.drupal.org/api/group/batch/6

1 голос
/ 19 марта 2010

Если вы генерируете PDF или CSV, вам не следует использовать нативные функции Drupal. Как насчет записи в выходной файл внутри вашего цикла while? Таким образом, только один набор результатов находится в памяти в данный момент времени.

0 голосов
/ 21 марта 2010

Способ, которым я обращаюсь к таким огромным отчетам, состоит в том, чтобы генерировать их с помощью php cli / Java / CPP / C # (т. Е. CRONTAB) + использовать опцию небуферизованного запроса, которую имеет mysql.
После создания файла / отчета на диске вы можете дать ссылку на него ...

0 голосов
/ 21 марта 2010

Следует также помнить, что в PHP5 (до версии 5.3) назначение массива новой переменной или передача его функции копирует массив и не создает ссылку. Возможно, вы создаете много копий одних и тех же данных, и если ни одна из них не сброшена или выходит из области видимости, они не могут быть собраны мусором для освобождения памяти. Где возможно, использование ссылок для выполнения операций с исходным массивом может сэкономить память

function doSomething($arg){
  foreach($arg AS $var)
    // a new copy is created here internally: 3 copies of data exist
    $internal[] = doSomethingToValue($var);
  return $internal;
  // $arg goes out of scope and can be garbage collected: 2 copies exist
}
$var = array();
// a copy is passed to function: 2 copies of data exist
$var2 = doSomething($var);
// $var2 will be a reference to the same object in memory as $internal, 
//  so only 2 copies still exist

если для $ var задано возвращаемое значение функции, старое значение можно собирать мусором, но не раньше, чем после присваивания, поэтому на короткое время все равно потребуется больше памяти

function doSomething(&$arg){
  foreach($arg AS &$var)
    // operations are performed on original array data: 
    // only two copies of an array element exist at once, not the whole array
    $var = doSomethingToValue($var);  
  unset($var); // not needed here, but good practice in large functions
}
$var = array();
// a reference is passed to function: 1 copy of data exists
doSomething($var);
0 голосов
/ 20 марта 2010

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

Проверьте это: http://api.drupal.org/api/function/pager_query/6

0 голосов
/ 19 марта 2010

Мне неудобно увеличивать предел памяти

Увеличение лимита памяти не означает, что каждый процесс php будет использовать этот объем памяти. Однако вы можете запустить клиентскую версию php с настраиваемым ограничением памяти - но это тоже не правильное решение ...

и у меня нет возможности использовать сохранение MySQL в outfile, чтобы просто обслуживать предварительно сгенерированный CSV

Тогда не сохраняйте все это в массиве - записывайте каждую строку в выходной буфер при извлечении его из базы данных (IIRC весь набор результатов буферизуется за пределами ограниченной памяти php). Или запишите его непосредственно в файл, а затем выполните перенаправление, когда файл будет завершен и закрыт.

С

0 голосов
/ 19 марта 2010

На данный момент вы храните все в массиве $table_rows.

Не можете ли вы очистить хотя бы части отчета, когда вы читаете его из базы данных (например, столько и много строк), чтобы освободить часть памяти? Я не понимаю, почему должна быть возможна только одновременная запись в csv.

...