AWS CentOS7环境下PHP7.2使用fputs写入大CSV文件速度过慢如何解决
相关配置说明
PHP 本身没有专门限制 fputs/fwrite 写入速率的配置项,Apache 配置也不会干预 PHP 本地文件写入的速率。你提到的 output_buffer 是 PHP 向 HTTP 客户端输出内容的缓冲区配置,和本地文件写入操作完全无关,无需调整该参数。
会间接影响文件写入速度的配置项有两类:
- PHP 层面的
memory_limit:如果脚本处理过程中内存占用超过限制,会触发频繁 GC 甚至 SWAP 交换,大幅拖慢写入速度。你当前环境有 4GB 内存,可先将memory_limit临时调整为1G测试,避免内存不足触发 SWAP 拖慢 IO。 - 系统与云服务层面的配置:AWS 云盘的性能规格、CentOS 磁盘挂载参数、IO 调度算法都会影响写入速度,如果你使用的是 GP2 类型云盘,在突发性能 credits 耗尽后会被限制到极低的写入速率,这是 AWS 环境大文件写入慢的常见原因。
核心性能瓶颈分析
你当前的自定义 CSV 写入函数实现,是导致写入速度慢的主要原因,存在三个明显的性能问题:
- 逐行调用
fputs:每生成一行就触发一次系统调用,3/4GB 的 CSV 通常有数百万行,累计系统调用开销极高。 - 逐字段正则匹配:每行每个字段都执行一次
preg_match正则校验,正则匹配本身开销较高,处理海量数据时累计开销非常大。 - 高频字符串拼接:每行生成时循环拼接字符串,会产生大量临时内存碎片,频繁触发内存分配释放,拖慢处理效率。
优化方案
代码层面优化(优先调整)
- 增加用户态写入缓冲区:不要每生成一行就写入磁盘,攒够 8~16MB 数据后一次性写入,大幅减少系统调用次数,参考修改逻辑如下:
// 定义缓冲区大小为16MB,可根据实际内存调整 define('CSV_WRITE_BUFFER_SIZE', 16 * 1024 * 1024); $csvBuffer = ''; // 脚本结束时自动刷入剩余缓冲区内容 register_shutdown_function(function() use ($fp, &$csvBuffer) { if (strlen($csvBuffer) > 0) { fputs($fp, $csvBuffer); } }); function fputcsv_content($fp, $array, $delimiter=",", $eol="\n") { global $csvBuffer; $line = ""; foreach($array as $value) { $value = trim($value); $value = str_replace("\r\n", "\n", $value); // 用strcspn替代正则匹配,性能提升3~5倍 if (strcspn($value, $delimiter."\"\n\r") !== strlen($value)) { $value = '"'.str_replace('"', '""', $value).'"'; } $line .= $value.$delimiter; } $line = substr($line, 0, -strlen($delimiter)) . $eol; $csvBuffer .= $line; // 缓冲区满后写入磁盘 if (strlen($csvBuffer) >= CSV_WRITE_BUFFER_SIZE) { fputs($fp, $csvBuffer); $csvBuffer = ''; return true; } return true; }
- 若无特殊自定义需求,直接使用 PHP 原生
fputcsv函数:原生函数为 C 语言实现,比 PHP 层面实现的写入逻辑性能高 10 倍以上。
环境层面优化
- 升级 AWS 云盘规格:将 GP2 云盘升级为 GP3 云盘,GP3 默认提供 3000 IOPS、125MB/s 吞吐量,不受容量基准和突发 credits 限制,适合大文件写入场景。
- 调整磁盘挂载参数:在
/etc/fstab中为数据盘挂载参数增加noatime,关闭文件访问时间记录,减少写入时的额外 IO 开销。 - 关闭不必要的后台进程:避免其他进程占用磁盘 IO 资源,影响写入速度。
内容的提问来源于stack exchange,提问作者Matias
相关产品推荐
相关产品推荐

