Как вы ограничиваете использование памяти PHP при обработке результатов запросов MySQL? - PullRequest
4 голосов
/ 08 октября 2009

Итак, у меня есть страница PHP, которая позволяет пользователям загружать CSV для того, что может быть целой кучей записей. Проблема в том, что чем больше результатов возвращает запрос MySQL, тем больше памяти он использует. Это не удивительно, но это создает проблему.

Я пытался использовать mysql_unbuffered_query (), но это не имело никакого значения, поэтому мне нужен какой-то другой способ освободить память, используемую, как я полагаю, ранее обработанными строками. Есть ли стандартный способ сделать это?

Вот прокомментированный журнал, который иллюстрирует, о чем я говорю:

// Method first called
2009-10-07 17:44:33 -04:00 --- info: used 3555064 bytes of memory

// Right before the query is executed
2009-10-07 17:44:33 -04:00 --- info: used 3556224 bytes of memory

// Immediately after query execution
2009-10-07 17:44:34 -04:00 --- info: used 3557336 bytes of memory

// Now we're processing the result set
2009-10-07 17:44:34 -04:00 --- info: Downloaded 1000 rows and used 3695664 bytes of memory
2009-10-07 17:44:35 -04:00 --- info: Downloaded 2000 rows and used 3870696 bytes of memory
2009-10-07 17:44:36 -04:00 --- info: Downloaded 3000 rows and used 4055784 bytes of memory
2009-10-07 17:44:37 -04:00 --- info: Downloaded 4000 rows and used 4251232 bytes of memory
2009-10-07 17:44:38 -04:00 --- info: Downloaded 5000 rows and used 4436544 bytes of memory
2009-10-07 17:44:39 -04:00 --- info: Downloaded 6000 rows and used 4621776 bytes of memory
2009-10-07 17:44:39 -04:00 --- info: Downloaded 7000 rows and used 4817192 bytes of memory
2009-10-07 17:44:40 -04:00 --- info: Downloaded 8000 rows and used 5012568 bytes of memory
2009-10-07 17:44:41 -04:00 --- info: Downloaded 9000 rows and used 5197872 bytes of memory
2009-10-07 17:44:42 -04:00 --- info: Downloaded 10000 rows and used 5393344 bytes of memory
2009-10-07 17:44:43 -04:00 --- info: Downloaded 11000 rows and used 5588736 bytes of memory
2009-10-07 17:44:43 -04:00 --- info: Downloaded 12000 rows and used 5753560 bytes of memory
2009-10-07 17:44:44 -04:00 --- info: Downloaded 13000 rows and used 5918304 bytes of memory
2009-10-07 17:44:45 -04:00 --- info: Downloaded 14000 rows and used 6103488 bytes of memory
2009-10-07 17:44:46 -04:00 --- info: Downloaded 15000 rows and used 6268256 bytes of memory
2009-10-07 17:44:46 -04:00 --- info: Downloaded 16000 rows and used 6443152 bytes of memory
2009-10-07 17:44:47 -04:00 --- info: used 6597552 bytes of memory

// This is after unsetting the variable. Didn't make a difference because garbage
// collection had not run
2009-10-07 17:44:47 -04:00 --- info: used 6598152 bytes of memory

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

Идеи

Вот код по запросу:

    $results = mysql_query($query);

    Kohana::log('info', "used " . memory_get_usage() . " bytes of memory");                

    $first = TRUE;
    $row_count = 0;

    while ($row = mysql_fetch_assoc($results)) {
        $row_count++;
        $new_row = $row;

        if (array_key_exists('user_id', $new_row)) {
            unset($new_row['user_id']);
        }

        if ($first) {
            $columns = array_keys($new_row);
            $columns = array_map(array('columns', "title"), $columns);
            echo implode(",", array_map(array('Reports_Controller', "_quotify"), $columns));
            echo "\n";
            $first = FALSE;
        }

        if (($row_count % 1000) == 0) {
            Kohana::log('info', "Downloaded $row_count rows and used " . memory_get_usage() . " bytes of memory");                
        }

        echo implode(",", array_map(array('Reports_Controller', "_quotify"), $new_row));
        echo "\n";
    }

Ответы [ 4 ]

2 голосов
/ 08 октября 2009

Дальнейшее профилирование показывает, что проблема заключается в утечке памяти где-то. Я сократил код до его самой простой формы, и использование памяти не увеличивается с каждой итерацией. Я подозреваю, что это Kohana (фреймворк, который я использую).

0 голосов
/ 27 сентября 2010

Спасибо за ваш вопрос с помощью mysql_unbuffered_query () решил мою проблему нехватки ОЗУ с PHP и MYSQL, работающим с большим набором данных.

Неустранимая ошибка PHP: допустимый объем памяти в 134217728 байт исчерпан (попытался выделить 32 байта) в /content/apps/application_price.php в строке 25

0 голосов
/ 08 октября 2009

Вы на самом деле периодически сбрасываете данные? Обычная буферизация в PHP может быть довольно вредной для долго выполняющегося кода, поскольку существует множество копий данных между клиентом MySQL, вашими переменными и системой вывода. Прошло несколько лет, но я в последний раз вспоминаю что-то вроде этого в скелетном коде:

ob_end_flush()
mysql_unbuffered_query()
while ($row = mysql_fetch…) {
   … do something …   

   flush(); // Push to Apache
   unset($row, … all other temporary variables …);
}
0 голосов
/ 08 октября 2009

Это "живая" загрузка? Под этим я подразумеваю передачу этого клиенту, когда вы генерируете CSV? Если это так, то есть несколько вещей, которые вы можете сделать:

  1. Не использовать буферизацию вывода. Это сохраняет все в памяти до тех пор, пока вы не очистите его явно или неявно (по окончании сценария), что будет использовать больше памяти;
  2. Когда вы читаете строки из базы данных, запишите их клиенту.

Кроме этого, нам, вероятно, нужно увидеть некоторый скелетный код.

...