debugging长时间运行的PHP脚本

我有php脚本运行作为一个cron工作,广泛使用第三方代码。 脚本本身有几千LOC。 基本上它是数据导入/处理脚本。 (JSON到MySQL,但它也使得很多的HTTP调用和一些SOAP)。

现在,业绩随着时间而下降。 当用几条logging(大约100条)进行testing时,性能是好的,在10-20分钟内完成。 运行整个import(约1600条logging)时,一条logging平均import时间稳定增长,整个工作时间超过24小时,至less比预期长5倍。

内存似乎不是一个问题,使用量增长应该没有意外的高峰。

所以,我需要debugging才能find瓶颈。 这可能是脚本,底层代码库,PHP本身,数据库,操作系统或networking部分的一些问题。 现在我怀疑某种地方的caching行为不好,几乎有100%的错过率。

我不能使用XDebug,configuration文件增长得太快,无法处理。

所以问题是: 我怎样才能debugging这种脚本?

PHP版本:5.4.41操作系统:Debian 7.8如果需要,我可以拥有root权限,并安装这些工具。 但它是生产服务器,理想的debugging不应该太扰乱。

分析工具:

有一个名为Blackfire的PHP分析工具,目前正处于公测阶段。 有关于如何配置CLI应用程序的具体文档。 一旦你收集了配置文件,你就可以在一个漂亮的用户界面中通过时间测量来分析应用程序控 黑火工具

内存消耗可疑:

内存似乎不是一个问题,使用量增长应该没有意外的高峰。

越来越多的内存使用情况实际上听起来可疑! 如果当前数据集不依赖于导入的所有以前的数据集,那么越来越多的内存可能意味着所有导入的数据集都保存在内存中,这是不好的。 PHP也可能经常尝试垃圾收集,只是为了发现没有什么东西可以从内存中删除。 尤其是长时间运行的CLI任务受到影响,因此请务必阅读发现该行为的博客帖子 。

是的,它可能你可以使用Kint(PHP调试脚本)

它是什么? Kint for PHP是一款旨在以最佳方式呈现调试数据的工具。

换句话说, 它是类固醇上的var_dump()和debug_backtrace()。 易于使用,但功能强大,可定制。 对您的开发工具箱的重要补充。

还是输了? 你用它来看看里面的变量。

在这里输入图像说明

充当debug_backtrace替换器 在这里输入图像说明

你可以在这里或这里 下载

全面的文件和帮助在这里

另外,它也支持几乎所有的PHP框架

  • Drupal的
  • Symfony的
  • Symfony 2
  • WordPress的
  • 警予
  • 骨架
  • Zend框架

祝一切顺利…. :)

有三件事情想起来了:

  1. 设置一个IDE,以便您可以逐行调试PHP脚本
  2. 将一些日志记录添加到脚本
  3. 在MySQL中查找长时间运行的查询

调试选项#2是最简单的。 由于这是作为一个cron作业运行,所以你在脚本中添加了一堆echo

 <?php function log_message($type, $message) { echo "[{strtoupper($type)}, {date('dmY H:i:s')}] $message"; } log_message('info', 'Import script started'); // ... the rest of your script log_message('info', 'Import script finished'); 

然后将stdout传递到cron作业命令中的日志文件。

 01 04 * * * php /path/to/script.php >> /path/to/script.log 

现在,您可以在脚本中添加log_message('info|warn|debug|error', 'Message here') ,至少了解性能问题的位置。

调试选项#3只是在MySQL中直接调查工作。 其中一个查询可能需要很长时间,并且可能会显示在一个长时间运行的MySQL查询实用程序中。

使用strace从系统的角度来看程序基本上在做什么。 它是否挂在IO操作等? 当遇到任何类型的Linux应用程序的性能问题时, strace应该是你尝试的第一件事情。 没有人可以隐藏它! ;)

如果你应该发现程序在connectreadfrom和朋友等网络相关的调用中挂起,这意味着网络通信在连接或等待响应时在某个时刻挂起,而不是使用tcpdump来分析。

使用上述方法,您应该能够找出最常见的性能问题。 请注意,您甚至可以使用-p PID使用strace附加到正在运行的任务。


如果上面的方法没有帮助,我会使用xdebug来分析脚本。 您可以使用诸如KCachegrind工具分析探查器输出

虽然没有规定,如果我的猜测是正确的,你似乎是在一个一个地处理记录,但在一个大的克朗。

即抓取一个记录#1,不知何故,增加它的价值,重新格式化然后保存它,然后移动到记录#2

我会考虑打破大克隆。 即

克隆#1:抓住所有的记录,并缓存所有重要的数据本地(到该服务器)。 设置一个标志,如果这个阶段已经实现。

克隆#2:现在你有你需要的数据,munge和增值,缓存输出。 设置一个标志,如果这个阶段已经实现。

克隆#3:重新格式化数据并存储它。 删除所有的文件。

这种“分而治之”将缓解您的调试困境,并更好地了解实际发生的情况,并为您提供重新运行的机会,例如cron 2。

我必须多次这样做,对于我来说,日志记录是识别代码中的弱点,识别有关数据质量的糟糕的假设的关键,并且可以暗示延迟导致问题的位置。

过去在做网络繁重工作时,我遇到了奇怪的减速。 基本上,我发现在手动测试中,系统速度非常快,但是在无人看管的情况下运行得并不如我所希望的那么多。

在我的情况下,我发现的问题是,我有默认的网络超时到位,许多网络请求会简单的超时。

一般来说,虽然不是外部工具,但可以使用两个microtime(TRUE)请求之间的差异来计算代码段。 为了保持日志记录小,设置一个标志限制,并且只测试在每个这样的事件减少之后该标志还没有减少到零的时间。 您可以为单个代码段设置单独的标志,甚至可以在代码段中设置不同的时间限制。

 $flag['name'] = 10; // How many times to fire $slow['name'] = 0.5; // How long in seconds before it's a problem? $start = microtime(TRUE); do_something($parameters); $used = microtime(TRUE) - $start; if ( $flag['name'] && used >= $slow['name'] ) { logit($parameters); $flag['name']--; } 

如果输出URL或其他数据/事件需要很长时间才能处理,那么稍后您可以深入研究该特定项目,以了解是否可以发现它是如何在代码中造成麻烦的。

当然,这是假定单个项目正在引起你的问题,而不是简单地随着时间的推移普遍放缓。

编辑:

我(现在)看到它是一个生产服务器。 这使编辑代码不太愉快。 您可能希望与代码集成一个最小的进程,具有测试逻辑,并可能支持标签/标志和数量在外部文件中。

 setStart('flagname'); // Do stuff to be checked for speed here setStop('flagname',$moredata); 

为了最大程度的健壮性,方法/功能必须确保它们处理未知标签,缺少参数等等。

xdebug_print_function_stack是一个选项,但你也可以做的是创建一个“函数轨迹”。有三种输出格式。 一个意思是人类可读的踪迹,另一个更适合于计算机程序,因为它更容易解析,最后一个使用HTML格式化的痕迹

 http://www.xdebug.org/docs/execution_trace 

好的,基本上你有两种可能性 – 要么是无效的PHP代码,要么是无效的MySQL代码。 从你所说的来看,可能是分别在索引表中插入了很多记录,导致插入时间飞快。 您应该禁用索引并在插入后重建它们,或优化插入代码。

但是,关于工具。

您可以配置系统自动记录缓慢的MySQL查询: https : //dev.mysql.com/doc/refman/5.1/en/slow-query-log.html

你也可以用PHP脚本来做同样的事情,但是你需要一个PHP-FPM环境(你可能有Apache)。 https://rtcamp.com/tutorials/php/fpm-slow-log/

这些工具非常强大和多功能。

PS 10-20分钟100个记录看起来像一个很多。

您可以使用https://github.com/jmartin82/phplapse记录应用程序活动的确定时间&#x3002;

例如,在n次迭代之后开始记录:

 phplapse_start(); 

并在下一次迭代中停止它:

 phplapse_stop(); 

有了这个过程,当所有的工作看起来都很慢时,你就创建了一个执行快照

(我是项目的作者,不要犹豫,联系我来改进功能)

我有一个类似的东西每晚运行(一个cron工作来更新我的数据库)。 我发现最可靠的调试方法是在数据库中建立一个日志表,并定期插入/更新一个包含多维数组的json字符串,以及关于每条记录的信息,以及关于每条记录想知道的有用信息。 这样,如果你的cron工作没有完成,你仍然有详细的信息,关于它的起步和沿途发生了什么。 然后,您可以编写一个简单的页面来提取json字符串,将其重新转换为数组,并将有用的数据打印到页面上,包括计时和通过的测试等。当您看到某个问题时,您可以集中精力放置更多信息区域放入json字符串中。

如果php或mysql的CPU使用率是瓶颈的话,常规的“top”命令可以显示你。 如果不是这样,那么延迟可能是由http调用引起的。

如果mysqld的CPU使用率较低,但不变,则可能是磁盘使用瓶颈。

此外,您可以通过安装和使用“速度计”或其他工具来检查您的带宽使用情况。