PHP分批读取大CSV文件(20万条)及指定行范围读取问题
PHP 高效分批读取大CSV文件的正确实现
我太懂处理超大CSV的痛苦了——20万条记录逐条插入简直是噩梦,分批读取+批量插入才是正确的打开方式。你之前尝试的跳过方法失效,主要是因为fgets没法正确识别CSV中带换行的字段(比如被引号包裹的多行内容),导致文件指针跑偏了。下面给你一套靠谱的实现方案:
核心思路
利用fgetcsv严格按照CSV规范解析每一行,每次读取指定批次的记录(比如10000条),处理完一批后继续读取下一批,直到文件结束。同时配合数据库批量插入操作,能把效率提升N倍。
完整代码示例
<?php // 配置参数 $csvFilePath = 'your_large_file.csv'; $batchSize = 10000; // 每批次读取的记录数 $delimiter = ','; // CSV分隔符,根据你的文件实际情况调整 // 打开CSV文件 $handle = fopen($csvFilePath, 'r'); if (!$handle) { die("无法打开目标CSV文件"); } // 读取表头(如果你的CSV包含表头行的话,不需要可以注释掉) $header = fgetcsv($handle, 0, $delimiter); // 循环分批读取并处理 while (!feof($handle)) { $batchData = []; // 读取当前批次的记录 for ($i = 0; $i < $batchSize; $i++) { $row = fgetcsv($handle, 0, $delimiter); if (!$row) { break; // 文件已读完,跳出当前批次循环 } // 可选:将行数据与表头关联成键值对数组,方便后续处理 $batchData[] = array_combine($header, $row); // 如果不需要表头关联,直接存原始数组即可:$batchData[] = $row; } // 处理当前批次的数据(比如批量插入数据库) if (!empty($batchData)) { processBatch($batchData); echo "已完成 " . count($batchData) . " 条记录的处理\n"; } } fclose($handle); echo "所有记录处理完成!"; /** * 批量处理数据的示例函数(以MySQL批量插入为例) * @param array $batchData 待处理的批次数据 */ function processBatch($batchData) { // 替换为你的数据库连接信息 $pdo = new PDO('mysql:host=localhost;dbname=your_database;charset=utf8mb4', 'username', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 关闭自动提交,用事务包裹批量操作,提升插入效率 $pdo->beginTransaction(); // 构造批量插入SQL语句(根据你的表结构调整字段) $fields = implode(', ', array_keys($batchData[0])); $valuePlaceholders = rtrim(str_repeat('(' . implode(', ', array_fill(0, count($batchData[0]), '?')) . '), ', count($batchData)), ', '); $sql = "INSERT INTO your_table ($fields) VALUES $valuePlaceholders"; $stmt = $pdo->prepare($sql); // 扁平化数据数组,适配PDO的execute参数要求 $flatData = []; foreach ($batchData as $row) { $flatData = array_merge($flatData, array_values($row)); } $stmt->execute($flatData); // 提交事务 $pdo->commit(); } ?>
关键细节说明
为什么不用
fgets跳过行?
CSV文件中经常会出现带引号的字段包含换行的情况,fgets只会按换行符拆分内容,无法识别这种合法的CSV行结构,导致文件指针定位错误。而fgetcsv会严格遵循CSV规范解析每一行,确保指针位置始终正确。批次大小的调整
10000条是比较均衡的数值,你可以根据服务器内存和数据库性能灵活调整:内存充足的话,适当增大批次能减少数据库交互次数;内存紧张则调小批次,避免内存溢出。数据库效率优化
- 一定要用批量插入语句(比如代码中的多值INSERT),而不是逐条执行INSERT,这是提升插入效率的核心。
- 用事务包裹批量操作,关闭自动提交,能大幅减少磁盘IO的次数。
- 如果是MySQL,还可以临时调整
innodb_flush_log_at_trx_commit=2(测试环境)或增大bulk_insert_buffer_size参数进一步优化。
解决你之前的问题
你之前尝试的手动跳过行的思路,本质是想通过指针定位跳过前面的记录,但因为fgets不识别CSV的复杂行结构,导致指针跳错位置。上面的代码不需要手动跳过,而是自然循环读取每一批,直到文件结束,完全避免了指针定位的问题。
内容的提问来源于stack exchange,提问作者user9288207
相关产品推荐
相关产品推荐

