【问题标题】:Writing a very large CSV file From DB output in PHP从 PHP 中的 DB 输出写入一个非常大的 CSV 文件
【发布时间】:2012-01-14 19:51:57
【问题描述】:

我有一个每秒收集的传感器数据数据库。客户希望能够以 CSV 格式下载 12 小时的块 - 这一切都完成了。

遗憾的是,输出不是直接数据,需要在创建 CSV 之前进行处理(部分以 JSON 形式存储在数据库中) - 所以我不能只转储表格。

所以,为了减少负载,我认为第一次下载文件时,我会将其缓存到磁盘,然后再有任何请求只需下载该文件。

如果我不尝试编写它(使用 file_put_contents,FILE_APPEND),并且只是回显每一行,它很好,但是编写它,即使我给脚本 512M 它也会耗尽内存。

这样就可以了

while($stmt->fetch()){
    //processing code
    $content = //CSV formatting
    echo $content;
}

这不是

while($stmt->fetch()){
    //processing code
    $content = //CSV formatting
    file_put_contents($pathToFile, $content, FILE_APPEND);
}

似乎我什至认为我在每一行都调用 file_put_contents,它把它全部存储到内存中。

有什么建议吗?

【问题讨论】:

  • 你有没有试过用 fwrite() 来逐行写?

标签: php mysql csv


【解决方案1】:

问题在于 file_put_contents 试图一次转储整个内容。相反,您应该在格式中循环并使用 fopen、fwrite、fclose。

while($stmt->fetch()){
    //processing code
    $content[] = //CSV formatting
    $file = fopen($pathToFile, a);
    foreach($content as $line)
    {
        fwrite($file, $line);
    }
    fclose($file);
}

这将限制在任何给定时间试图在数据中折腾的数据量。

【讨论】:

  • 虽然基本正确,但请注意原始中如何使用 echo $content 所以它只是 $content = // CSV formatting ; fwrite($file, $content); 并将 fopenfclose 移出主循环。
  • 你确定一直打开和关闭文件真的有必要吗?否则我同意,尝试使用 fwrite 一次在线写入文件。
  • 比赛条件有人吗?如果第二个用户加载它而另一个用户仍在等待它加载怎么办?我想数据损坏是可能的,甚至可能因为在办公环境中,用户可能会同时看到这样的东西。
  • @sakatc 然后我会缓存文件,所以如果有一个不早于 X 的文件,则提供该文件。
【解决方案2】:

我完全同意一次写一行,这样你永远不会遇到内存问题,因为一次加载到内存中的行不会超过 1 行。我有一个做同样的应用程序。然而,我发现这种方法的一个问题是文件需要永远完成写入。所以这篇文章是为了支持已经说过的内容,同时也请大家对如何加快速度提出意见?例如,我的系统针对抑制文件清理数据文件,因此我一次读取一行并在抑制文件中查找匹配项,然后如果未找到匹配项,则将该行写入新的清理文件.然而,一个 50k 行的文件大约需要 4 个小时才能完成,所以我希望能找到更好的方法。我已经尝试了几种方法,此时我现在将整个抑制文件加载到内存中,以避免我的主读取循环必须通过抑制文件中的每一行运行另一个循环,但即使这样仍然需要几个小时。

因此,到目前为止,逐行是管理系统内存的最佳方式,但我希望获得 50k 行文件(行是电子邮件地址以及名字和姓氏)的处理时间,以完成运行如果可能的话,少于 30 分钟。

仅供参考:抑制文件大小为 16,000 kb,由 memory_get_usage() 告知的脚本使用的总内存约为 35 megs。

谢谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 1970-01-01
    • 2019-07-09
    • 1970-01-01
    相关资源
    最近更新 更多