我有一个大的CSV文件,我需要分成4个部分,然后将数据发送到数据库;我遇到的问题是CSV文件可能是1GB +(并且它们可能一次多于一个),这会产生各种时间延迟和内存问题。
我想帮助我改进和加快这个过程。
我测试的文件有45000个记录; ~10mb文件。
现在我正在将文件加载到数组中,使用大约是文件大小的3倍,因此对于10mb文件我们说的是30mb内存;我希望通过逐行读取文件来减少对30mb内存的需求,但是要看一下这个内存。
处理部分相当简单,基本上我循环遍历数据数组。
最后一部分是将数据保存到数据库中,由于将数据保存到数据库所花费的时间,目前的主要问题在于此。
最初我尝试创建一个大字符串并将其全部发送到DB中,但22k记录需要大约2GB的ram内存;即使这个过程相当快,我仍然会耗尽内存。
我目前使用的方法是一次添加行,这不需要太多内存,但处理45k记录可能需要近一个小时。
我的下一步是查看创建一个带有完整查询列表的mysql文件,并通过mysql导入函数将所有查询导入MySQL。
如果有人可以就如何进一步提高脚本的性能提出建议,那将是很好的。
更新1 现在我不再将文件加载到内存中并逐行处理文件。处理大文件并不需要多长时间。处理500MB数据文件需要几秒钟。
在导入数据库方面,我有两种方法可以尝试:
目前我正在考虑尝试" LOAD DATA INFILE"但我需要做一个解决方法,因为有几个表有一对多关系,我需要最后插入id。
更新2
加载数据本地Infile设法解决了一些硬盘问题;我还必须使用SplFileObject让我更容易阅读文件。我为Load Data Local Infile创建的文件仍然很大,但它比以前更好。
目前我不得不在文件中执行循环并存储信息所在的起点/终点,同时执行此操作我创建除了需要外键的表之外的所有表。所以我做的是:
表A和表B有一对多关系:表A是在第一个循环(循环文件)中创建的,另外我们在表A的单元格中存储表B的参数。
在第二个循环中,我们遍历表A并从单元格中获取数据并在表B中创建一组新行。
在第一个循环中,我不得不解析表B的参数,以提高第二个循环的性能。
我在第二个循环中有很多foreach / for语句,因为第二个循环需要x4才能完成。
10MB文件的当前表现如下:
然而,随着文件变大,40MB文件:
,性能似乎会恶化如果我不在第一个循环中解析参数,表B性能为10MB:
40MB文件的性能在第一个循环中很好,但在第二个循环中很糟糕。
在第一次和第二次循环中没有任何foreach循环,处理10MB需要大约3-4秒
第一个循环中的foreach循环示例,用于组织表B的参数:
public function parseRawParam($line, $titles) {
$params = [];
$line = str_replace("\n", "", $line);
$rows = explode(",", $line);
for($row_i = 4; $row_i < count($rows); $row_i++) {
if(strlen(trim($rows[$row_i])) < 1) {
break;
}
$params[$titles[$row_i]] = $rows[$row_i];
}
return $params;
}
$ line可能包含4到160之间的参数。
第二个循环有一个foreach循环,它处理params的插入,这需要很多时间:
public function insertParam($record_id, $params) {
$sql = "";
foreach ($params as $param => $value) {
$sql = '"' . $record_id . '","' . str_replace("'", "\'", trim($param)) . '","' . trim($value) . '";';
}
return $sql;
}
我一直在关注不同PHP版本之间的性能,PHP7与php 5.6相比要快得多,所以我希望升级PHP版本并提高性能。
答案 0 :(得分:0)
对于数据库,您需要使用LOAD DATA INFILE。如果要对CSV数据进行任何数据更改,则应该能够构建行的文本文件,以便一次性批量插入到数据库表中。这应该减少单个表格行插入的锁定,这是很昂贵的。