ограничение памяти php и чтение / запись временных файлов - PullRequest
2 голосов
/ 25 ноября 2011

используя функцию ниже, я извлекаю строки из таблиц, кодирую их, затем помещаю их в формат csv. Мне интересно, есть ли более простой способ предотвратить чрезмерное использование памяти. Я не хочу полагаться на ini_set. Я считаю, что потребление памяти вызвано чтением временного файла и его распаковкой. Я бы хотел иметь ограничение в 64 Мб оперативной памяти для работы. Есть идеи? Спасибо!

function exportcsv($tables) {
    foreach ($tables as $k => $v) {
        $fh = fopen("php://temp", 'w');
        $sql = mysql_query("SELECT * FROM $v");
        while ($row = mysql_fetch_row($sql)) {
            $line = array();
            foreach ($row as $key => $vv) {
                $line[] = base64_encode($vv);
            }
            fputcsv($fh, $line, chr(9));
        }
        rewind($fh);
        $data = stream_get_contents($fh);
        $gzdata = gzencode($data, 6);
        $fp = fopen('sql/'.$v.'.csv.gz', 'w');
        fwrite($fp, $gzdata);
        fclose($fp);
        fclose($fh);
    }
}

Ответы [ 4 ]

2 голосов
/ 25 ноября 2011

не проверено, но, надеюсь, вы понимаете,

function exportcsv($tables) {
    foreach ($tables as $k => $v) {
        $fh = fopen('compress.zlib://sql/' .$v. '.csv.gz', 'w');
        $sql = mysql_unbuffered_query("SELECT * FROM $v");
        while ($row = mysql_fetch_row($sql)) {
            fputcsv($fh, array_map('base64_encode', $row), chr(9));
        }
        fclose($fh);
        mysql_free_result($sql);
    }
}

точки редактирования представляют собой использование mysql_unbuffered_query и использование потока сжатия php.Обычный mysql_query () буферизует весь набор результатов в память.а использование потока сжатия избавляет от необходимости еще раз буферизовать данные в php-памяти в виде строки перед записью в файл.

2 голосов
/ 25 ноября 2011

Загрузка всего файла в память через stream_get_contents(), вероятно, то, что вас убивает.Вам нужно не только хранить данные base64 (которые обычно составляют около 33% от их необработанного содержимого), но и накладные расходы csv.Если проблема с памятью, подумайте о том, чтобы просто вызвать приложение gzip из командной строки вместо gzipping внутри PHP, что-то вроде:

... database loop here ...
exec('gzip yourfile.csv');

И, вероятно, вы можете немного лучше оптимизировать вещи внутри цикла DB, икодировать на месте, а не создавать новый массив для каждой строки:

while($row = mysql_fetch_row($result)) {
   foreach ($row as $key => $val) {
       $row[$key] = base64_encode($val);
       fputcsv($fh, $row, chr(9));
   }
}

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

1 голос
/ 25 ноября 2011

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

fflush($fh);

Также, вместо того, чтобы сжать весь файл, вы можете вставить строку gzipпострочно, используя

$gz = gzopen ( $fh, 'w9' );
gzwrite ( $gz, $content );
gzclose ( $gz );

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

0 голосов
/ 25 ноября 2011

Я нашел это предложение для сжатия кусками на http://johnibanez.com/node/21

Похоже, это не составит труда изменить в ваших целях.

function gzcompressfile($source, $level = false){ 
    $dest = $source . '.gz'; 
    $mode = 'wb' . $level; 
    $error = false; 

    if ($fp_out = gzopen($dest, $mode)) { 
        if ($fp_in = fopen($source, 'rb')) { 
            while(!feof($fp_in)) {
                gzwrite($fp_out, fread($fp_in, 1024*512)); 
            }
            fclose($fp_in); 
        } 
        else
            $error=true; 

        gzclose($fp_out); 
    } 
    else 
        $error=true; 

    if ($error)
        return false; 
    else
        return $dest;
} 
...