我目前遇到的问题是,当使用 PHP 写入 csv 文件时,出现“允许的内存大小为 134217728 字节已耗尽”。在投入更多硬件之前,我宁愿与您确认我正在做的事情是否是最好的方法。
我正在做的是从数据库中获取具有偏移量和限制的数据块,然后使用 fputcsv 将结果写入文件中。
这就是我正在做的事情:
检查目标文件夹是否存在,并通过锁定文件检查导出是否已在运行。如果这些检查通过,我将使用以下命令打开文件句柄:
$this->handle = @fopen($filePath, 'wb+');
$resultData = $this->getDatasets($offset);
while ($resultData) {
foreach ($resultData as $dataArray) {
$this->writer->addRow($dataArray);
++$offset;
}
$resultData = $this->getDatasets($offset);
}
$this->writer->close();
getDatasets() 始终从数据库获取 100 个数据集。 addRow() 正在使用 fputcsv
public function addRow(array $row): bool|int
{
if (false !== $this->handle) {
return fputcsv($this->handle, $row, ';', '"', '\\', "\n");
}
return false;
}
public function close(): bool
{
if ($this->isOpen()) {
return fclose($this->handle);
}
return false;
}
我认为通过这种设置,内存中的数据集永远不会超过 100 个,而且由于每一行都写入文件中,所以应该没有问题。但也许打开的文件完全在内存中?
该文件被写入文件系统,因为它应该每天只生成一次,而不是针对每个请求生成。
我可以改进什么来避免内存问题?
我们谈论的是 40000 个数据集。是的,这是大量数据,但不是数百万个数据集。
设置:php 8.2,nginx服务器内存:1024M
使用原则获取数据 symfony 项目:
public function getDatasets(int $offset = 0, int $chunkSize = 100): array
{
$foos = $this->fooRepo->findBy(
['active' => true],
['familyName' => 'ASC'],
$chunkSize,
$offset
);
$i = 1;
$resultData = [];
/** @var Foo $foo */
foreach ($foos as $foo) {
$resultData[] = $foo->__toArray();
++$i;
}
$total = $offset + $i;
return $resultData;
}
一位同事帮助我找出了问题所在。上面发布的代码完全没问题。问题在于该学说将实体数据保留在内存中。
所以我在这里发布改进的方法 getDatasets() :
public function getDatasets(int $offset = 0, int $chunkSize = self::CHUNK_SIZE): array
{
$suspects = $this->suspectRepo->findBy(
['active' => true],
['familyName' => 'ASC'],
$chunkSize,
$offset
);
$i = 1;
$resultData = [];
/** @var Suspect $suspect */
foreach ($suspects as $suspect) {
$resultData[] = $suspect->__toArray();
// detach empties each entity information from the memory after use
$this->entityManager->detach($suspect);
// to be sure I also unset the object via php
unset($suspect);
++$i;
}
return $resultData;
}
通过这些调整,我的代码确实运行没有问题。所以是的,我的问题中的代码很好,并且通过数据管理的这些改进,它可以按需要运行!