PHP内存限制和读/写临时文件


php memory limit and reading/writing temp files

使用下面的函数,我从表中提取行,对它们进行编码,然后将它们放入csv格式。我想知道是否有一种更简单的方法来防止高内存使用。我不想依赖于ini_set。我认为内存消耗是由于读取临时文件并将其压缩造成的。我希望能够有64mb的内存限制。什么好主意吗?谢谢!

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);
    }
}

未经测试,但希望您能理解

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内存中。

通过stream_get_contents()将整个文件拉入内存可能是杀死您的原因。您不仅需要保存base64数据(通常比原始内容大33%),还需要处理csv开销。如果内存是一个问题,可以考虑简单地调用命令行gzip应用程序,而不是在PHP内部进行gzip,如:

... 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));
   }
}

并不是说这将减少内存的使用-它只是一个单行数据,所以除非你处理的是巨大的记录字段,否则它不会有太大的影响。

你可以在那里插入一些刷新,目前你的整个php文件将保存在内存中,然后在最后刷新,但是如果你手动

fflush($fh);

也可以使用

逐行gzip来代替对整个文件进行gzip。
$gz = gzopen ( $fh, 'w9' );
gzwrite ( $gz, $content );
gzclose ( $gz );

这将逐行写入打包的数据,而不是创建整个文件然后压缩它。

我在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;
}