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

时间:2009-07-24 09:26:43

标签: mysql linux php

对于downvote仙女..我知道php是错误的语言...但我在外界限制下工作。鉴于:

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

我最初尝试在导入平面文件时使用ORM系统。即使小心地释放对象,该设计也存在大量的php内存泄漏问题。即使我确保有足够的内存,脚本也需要大约25天才能在我的桌面上运行。

我剥离了开销并重写了脚本以直接构建mysql命令。我从我的设计中删除了AUTO INCREMENT,因为这要求我作为Mysql输入的最后一个id是为了在数据点之间建立关系。我只是使用数据库ID的全局计数器,我从不进行任何查找,只需插入。

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

使用这些优化(希望他们帮助其他人)我让导入脚本在大约6个小时内运行。

我租用了一个虚拟实例,其RAM大5倍,处理器功率大约是我的桌面的5倍,并注意到它的速度完全相同。服务器运行该进程但具有CPU周期和RAM备用。也许限制因素是磁盘速度。但我有很多内存。我应该尝试以某种方式将文件加载到内存中吗?欢迎任何进一步优化处理大文件的php命令行脚本的建议!

5 个答案:

答案 0 :(得分:3)

你不会喜欢它但是......听起来你正在使用错误的语言来完成手头的任务。如果你想在速度上获得一些巨大的飞跃,那么下一步就是编译语言的端口。编译语言的运行速度比脚本语言快得多,因此您可以看到处理时间下降。

此外,您可以使用内置命令将数据转储到数据库中。 Postgres有一个(转储?加载?类似的东西),它将在制表符分隔的文本文件中读取,其列与表中的列匹配。这样您就可以专注于以正确的格式获取文本文件,然后使用一个命令将其吐入DB,并让它处理优化而不是您自己。

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

我们已经完成了与.net应用程序类似的事情,每天早上在每行上运行20Gb文件,在每行上执行RegExp,为唯一记录保留内存哈希,然后将新内容写入数据库。然后,我们使用Ruby脚本轻松吐出9000+个JS文件(这是最慢的部分)。我们过去也用Ruby编写了导入器,整个过程需要3个多小时,重新编写.net会在大约30-40分钟内完成整个过程,其中20个是慢速Ruby脚本(不值得再优化了)虽然它做得很好)。

答案 1 :(得分:3)

针对此类任务的几个重要设计建议:

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

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

另一种选择是使用批量插入。如果您的数据库不是本地的(例如,您通过网络连接),这可以提升。我认为(不确定)它也提供与交易相同的好处 - 甚至可能用于MyIsam表。

最后,如果没有其他工作,您可以从等式中删除php并使用LOAD DATA INFILE。您可能必须首先使用php或其他一些文本处理语言预处理文件(awk或sed具有非常好的性能配置文件)

答案 2 :(得分:2)

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

  • 您在PHP脚本中构建请求(取决于计算机的功能,但非常快)
  • 您必须将该请求发送到MySQL服务器(通过网络或本地套接字; 需要时间
  • MySQL服务器必须处理数据(存储它,创建索引,使用它的锁以确保安全,......)
  • 答案(“ok”,插入数据)必须返回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 - 它应该更快。

希望这有帮助,玩得开心!

答案 3 :(得分:1)

除了优化脚本之外,您建议尝试使用任何PHP加速器(例如:eaccelerator.net)。如果这没有帮助,我建议使用为此类任务制作的语言/平台。

答案 4 :(得分:0)

几乎所有其他答案都指出了。 PHP不太适合这种处理。

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

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

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

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

xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY );

您可以轻松查看自己代码的哪些特定部分需要花费很多时间。

HTH,其他任何一个人注定要继续强迫我用方钉钉住一根钉子。

-FT