PHP 写入 csv 文件耗尽内存

问题描述 投票:0回答:1

我目前遇到的问题是,当使用 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;
}
csv nginx doctrine out-of-memory php-8.2
1个回答
0
投票

一位同事帮助我找出了问题所在。上面发布的代码完全没问题。问题在于该学说将实体数据保留在内存中。

所以我在这里发布改进的方法 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;
    }

通过这些调整,我的代码确实运行没有问题。所以是的,我的问题中的代码很好,并且通过数据管理的这些改进,它可以按需要运行!

© www.soinside.com 2019 - 2024. All rights reserved.