优化php命令行脚本来处理大型平面文件

对于downvote仙女..我知道PHP是这个错误的语言…但我在外面的限制下工作。 鉴于:

我有一个大的平面文件,我需要在PHP中处理。 我把平面文件转换成mysql中的规范化数据库。 平面文件中有几百万行。

我最初尝试在导入平面文件时使用ORM系统。 即使在小心地释放对象的情况下,该devise也存在大量的PHP内存泄漏问题。 即使我确保有足够的内存,脚本将花费大约25天的时间在我的桌面上运行。

我剥离了开销并重写了脚本直接构buildmysql命令。 我从我的devise中删除了AUTO INCREMENT,因为这要求我将Mysqlinput的最后一个id作为数据点之间的关系。 我只是使用一个全局计数器的数据库IDS,而我从来没有做任何查找,只是插入。

我使用unix split命令来制作很多小文件,而不是一个大文件,因为一次又一次地使用文件指针会带来内存开销。

使用这些优化(希望他们帮助别人)我得到的import脚本运行大约6个小时。

我租用了一个虚拟实例,其内存是5倍,比我的桌面多了5倍的处理器能力,并注意到它的速度完全相同。 服务器运行进程,但有CPU周期和RAM备用。 也许限制因素是磁盘速度。 但是我有很多内存。 我应该尝试加载文件到内存不知何故? 任何build议进一步优化PHP命令行脚本处理大文件,欢迎!

你不会喜欢它,但是…听起来你正在使用错误的语言来完成任务。 如果你想在速度上有一个巨大的飞跃,那么一个编译语言的端口将是下一步要做的。 编译语言的运行速度远远超过了脚本语言,所以你会看到你的处理时间下降。

另外,您可以使用build in命令将数据转储到数据库中。 Postgres有一个(转储?加载?类似的东西),这将读取一个制表符分隔的文本文件谁列与表中的列匹配。 这将允许你只关注获取正确格式的文本文件,然后用一个命令将它分散到数据库中,让它处理优化,而不是你自己。

你已经做了正确的事敲敲ORM的头,分裂文件不应该需要,虽然你的文本文件阅读器应该只是在内部使用缓冲区,所以“应该”无关紧要,但我不是一个* nix的家伙所以在这方面可能是错误的。

我们已经做了一些类似的.net应用程序,每天早上通过20Gb的文件在每行上执行RegExp,保留内存哈希以获得唯一记录,然后向数据库中插入新的文件。 然后,我们使用Ruby脚本来释放9000多个JS文件(这是最慢的部分)。 我们过去也有用Ruby编写的导入程序,整个过程花了3个多小时,重新编写.net运行整个过程约30-40分钟,其中20个是缓慢的Ruby脚本(不值得再优化尽管它足够好)。

一些重要的设计建议,这样的任务:

不要一次将整个文件读入内存。 使用一个文件指针,并阅读合理的块(比如,几千字节..取决于平均记录大小)。 然后处理每个记录并将缓冲区移出。 从你的描述中我不确定你是否已经这样做了。

如果您的mysql存储类型支持事务(该表必须是InnoDB),则可以使用它们进行优化。 启动一个事务并处理f.ex. 100k行,然后通过交易刷新并打开一个新的。 这是有效的,因为MySql只会更新索引一次,而不是每一行。

另一种选择是使用批量插入。 如果你的数据库不是本地的(例如你通过网络连接),这可以提升。 我认为(不知道),它也给交易同样的好处 – 甚至可能为MyIsam表。

最后,如果没有别的办法,你可以从等式中删除PHP,并使用LOAD DATA INFILE 。 您可能需要先使用php或其他文本处理语言(awk或sed具有非常好的性能配置文件)预先处理文件,

您花费的时间,在不同的机器之间相似,可能是因为PHP脚本和MySQL服务器之间的通信:对于每个MySQL请求:

  • 你在PHP脚本中建立请求(取决于机器的功率,但真的很快)
  • 您必须将该请求发送到MySQL服务器(通过网络或本地套接字; 需要时间
  • MySQL服务器必须处理数据(存储它,创建索引,使用它的锁来实现安全性…)
  • 答案(“确定”,插入的数据)必须返回到PHP(相同:网络或套接字,即慢)
  • 它每一次都这样做。

什么需要时间可能不是真的在PHP方面, 最有可能的是,在PHP和MySQL之间 – 可能没有太多可以做的事情。

如果你有一个相当强大的机器,我会建议的是:

  • 把你的数据拆分成X(不是太多人,比如说X = 6)
  • 修改您的PHP脚本,以便您可以并行启动它6次,并将其作为参数编号。
  • 启动脚本6次。

脚本的第一个并行执行将处理少6倍的数据; 对于其他人来说也是一样的……而且他们会同时工作……所以,最终整个过程需要花费的时间可能减少4倍:-)

它可能不会花费6倍的时间:并行化的工作意味着在机器上增加一些负载,并且MySQL将会有一些并发请求 – 但是只有几个并行进程,那就没问题了。

作为一个方面说明:从PHP做这可能不是最好的事情。 这是另一种我能想到的方式:

  • 使用脚本(如PHP或Perl或shell或其他)来:
    • 读取输入文件
    • 生成插入请求(但不发送到MySQL服务器)
    • 把这些请求写入一个文件
  • 当数百万行的所有请求都在文件中时:
    • 一次性启动该文件到MySQL。
    • 像这样的事情可以在命令行中执行:“ mysql --host=HOST --user=USER --password=PASSWORD DATABASE_NAME < inserts-commands.sql

这样,只要确保文件中的SQL请求是正确的,然后MySQL就可以一次性导入所有内容:对于每个请求,您不必从PHP到MySQL,这应该会更快。

希望这可以帮助,玩得开心!

除了从脚本的optmizations你会建议尝试任何PHP加速器(例如: eaccelerator.net )。 如果这没有帮助,我会建议使用一种语言/平台,这种任务。

几乎所有其他答案指出。 PHP对于这种处理并不理想。

尤其是现在HADOOP等人已经基本上把这种特定类型的任务完全并行化在云中。

但是某个地方可能需要像我一样使用PHP来处理大文件。

鉴于此,我应该指出,新的Facebook工具xhprof在命令行上工作得很好。

如果您发送以下命令来启动它:

 xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY ); 

你可以很容易地看到你自己代码的特定部分花了那么多时间。

HTH,其他任何人都注定要继续通过与我的方钉。

-FT