解析和处理大文件

时间:2016-11-25 13:34:39

标签: php mysql

我有一个大的CSV文件,我需要分成4个部分,然后将数据发送到数据库;我遇到的问题是CSV文件可能是1GB +(并且它们可能一次多于一个),这会产生各种时间延迟和内存问题。

我想帮助我改进和加快这个过程。

我测试的文件有45000个记录; ~10mb文件。

现在我正在将文件加载到数组中,使用大约是文件大小的3倍,因此对于10mb文件我们说的是30mb内存;我希望通过逐行读取文件来减少对30mb内存的需求,但是要看一下这个内存。

处理部分相当简单,基本上我循环遍历数据数组。

最后一部分是将数据保存到数据库中,由于将数据保存到数据库所花费的时间,目前的主要问题在于此。

最初我尝试创建一个大字符串并将其全部发送到DB中,但22k记录需要大约2GB的ram内存;即使这个过程相当快,我仍然会耗尽内存。

我目前使用的方法是一次添加行,这不需要太多内存,但处理45k记录可能需要近一个小时。

我的下一步是查看创建一个带有完整查询列表的mysql文件,并通过mysql导入函数将所有查询导入MySQL。

如果有人可以就如何进一步提高脚本的性能提出建议,那将是很好的。

更新1 现在我不再将文件加载到内存中并逐行处理文件。处理大文件并不需要多长时间。处理500MB数据文件需要几秒钟。

在导入数据库方面,我有两种方法可以尝试:

  1. 使用PDO,一次导入语句:大约需要1分钟处理5MB数据文件;很慢
  2. 使用mysql< file.sql,导入更快,大约需要1分钟处理10MB数据文件;哪个更好,但仍然很慢。权衡的是我需要生成可能非常大的.sql文件,此刻20MB数据文件将导致.sql文件大小约为600MB;
  3. 目前我正在考虑尝试" LOAD DATA INFILE"但我需要做一个解决方法,因为有几个表有一对多关系,我需要最后插入id。

    更新2

    加载数据本地Infile设法解决了一些硬盘问题;我还必须使用SplFileObject让我更容易阅读文件。我为Load Data Local Infile创建的文件仍然很大,但它比以前更好。

    目前我不得不在文件中执行循环并存储信息所在的起点/终点,同时执行此操作我创建除了需要外键的表之外的所有表。所以我做的是:

    表A和表B有一对多关系:表A是在第一个循环(循环文件)中创建的,另外我们在表A的单元格中存储表B的参数。

    在第二个循环中,我们遍历表A并从单元格中获取数据并在表B中创建一组新行。

    在第一个循环中,我不得不解析表B的参数,以提高第二个循环的性能。

    我在第二个循环中有很多foreach / for语句,因为第二个循环需要x4才能完成。

    10MB文件的当前表现如下:

    • 首次循环6秒
    • 第二次循环12秒
    • 平均总时间18-20秒

    然而,随着文件变大,40MB文件:

    ,性能似乎会恶化
    • 首次循环30秒
    • 第二次循环60秒
    • 平均总时间90-100秒

    如果我不在第一个循环中解析参数,表B性能为10MB:

    • 首次循环3秒
    • 第二次循环16秒
    • 平均总时间19-22秒

    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版本并提高性能。

1 个答案:

答案 0 :(得分:0)

对于数据库,您需要使用LOAD DATA INFILE。如果要对CSV数据进行任何数据更改,则应该能够构建行的文本文件,以便一次性批量插入到数据库表中。这应该减少单个表格行插入的锁定,这是很昂贵的。

http://dev.mysql.com/doc/refman/5.7/en/load-data.html