我有时会根据数据库信息创建大型csv文件,供用户下载-100k或更多行。在一些较大的文件上创建csv时,我似乎遇到了内存问题。下面是我目前如何处理csv创建的一个例子。
有办法解决这个问题吗?最初有3200万字节,后来改为6400万字节,但仍然存在问题。
//columns array
$log_columns = array(
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9'
);
//results from the db
$results = $log_stmt->fetchAll(PDO::FETCH_ASSOC);
$log_file = 'test.csv';
$log_path = $_SERVER['DOCUMENT_ROOT'].'/../user-data/'.$_SESSION['user']['account_id'].'/downloads/';
// if location does not exist create it
if(!file_exists($log_path))
{
mkdir($log_path, 0755, true);
}
// open file handler
$fp = fopen($log_path.$log_file, 'wb');
// write the csv column titles / labels
fputcsv($fp, $log_columns);
//are there any logs?
if($results)
{
//write the rows
foreach($results as $row)
{
//rows array
$log_rows = array(
$row['1'],
$row['2'],
$row['3'],
$row['4'],
$row['5'],
$row['6'],
$row['7'],
$row['8'],
$row['9']
);
//write the rows
$newcsv = fputcsv($fp, $log_rows);
}//end foreach
}
// there were no results so just return an empty log
else
{
$newcsv = fputcsv($fp, array('No results found.') );
}
//close handler
fclose($fp);
// if csv was created return true
if($newcsv)
{
return true;
}
更新:
使用while循环和fetch而不是foreach和fetchAll仍然会产生内存错误。
while($result = $log_stmt->fetch(PDO::FETCH_ASSOC))
如果我一次只加载一行,那怎么可能呢?
更新2:
我已经使用memory_get_usage();
进一步追踪到while循环
echo (floor( memory_get_usage() / 1024) ).' kb<br />';
在while循环开始之前,结果是4658kb,然后对于while循环的每次迭代,它每2-3个循环增加1kb,直到它达到允许的32748kb最大内存。
我能做些什么来解决这个问题?
更新3:
今天玩得更多。。。这种工作方式对我来说没有多大意义——我只能假设这是php的GC的一种奇怪行为。
场景1:我的查询获取所有80k行,并使用while循环输出它们。在获取查询后,使用的内存约为4500kb,然后在循环中输出的每两到三行增加1kb。内存并没有像以前那样被释放,而且在某个时刻没有足够的内存就会崩溃。
while($results = $log_stmt->fetch(PDO::FETCH_ASSOC))
{
echo $results['timestamp'].'<br/>';
}
场景2:我的查询现在是循环的,一次得到1000行,其中每个行都有一个循环输出。当它循环并在没有内存问题的情况下完成整个输出时,内存最大值为400k。
对于这个例子,我只使用了80次计数器,因为我知道有超过80k行要检索。事实上,很明显,我不得不采取不同的做法。
$t_counter = 0;
while($t_counter < 80)
{
//set bindings
$binding = array(
'cw_start' => $t_counter * 1000,
//some other bindings...
);
$log_stmt->execute($binding);
echo $t_counter.' after statement '.floor( memory_get_usage() / 1024 ).' kb<br />';
while($results = $log_stmt->fetch(PDO::FETCH_ASSOC))
{
echo $results['capture_timestamp'].'<br/>';
}
echo $t_counter.' after while'.floor( memory_get_usage() / 1024 ).' kb<br />';
$t_counter++;
}
所以我想我的问题是,为什么第一个场景的内存使用量不断增加,却什么都没有发布?在while循环中没有新的变量,所有的东西都被"重用"了。完全相同的情况发生在另一个循环中的第二个场景中。
fetchAll获取所有记录,这些记录不仅查询它并用fetch进行while循环,而且不需要在内存中加载所有结果集。
http://php.net/manual/en/pdostatement.fetch.php
然后我认为您应该尝试以位读取文件。读取它们并将其附加到一个csv文件中,这样可以在处理过程中释放内存。您可以进行计数(*),但尝试在多个集合之前找到总计数
我自己一直在使用php的csv,我甚至把它用作数据库系统(nosql)尝试
用于读取的csv代码<?php
$CSVfp = fopen("filename.csv", "r");
if($CSVfp !== FALSE) {
$con=1;
while(! feof($CSVfp))
{
do something
}?>
**csv code for writting **
<?php
$list = array
(
"edmond,dog,cat,redonton",
"Glenn,Quagmire,Oslo,Norway",
);$file = fopen("filename.csv","w");foreach ($list as $line)
{fputcsv($file,explode(',',$line));}fclose($file); ?>