调试长时间运行的PHP脚本

时间:2015-07-20 07:58:06

标签: php linux debugging

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

现在,性能随着时间的推移而降级。使用少量记录(大约100)进行测试时,性能良好,可在10-20分钟内完成。当运行整个导入(大约1600条记录)时,一条记录的平均导入时间稳步增长,整个过程需要超过24小时,所以至少比预期长5倍。

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

所以,我需要调试它才能找到瓶颈。它可能是脚本,底层代码库,php本身,数据库,操作系统或网络部分的一些问题。我现在怀疑某种缓存在一个不太好的情况下,几乎没有100%的错失率。

我不能使用XDebug,配置文件增长得太快而无法治疗。

所以问题是:我该如何调试这种脚本?

PHP版本:5.4.41 操作系统:Debian 7.8 如有必要,我可以拥有root权限,并安装工具。但它是生产服务器,理想情况下调试不应该太干扰。

11 个答案:

答案 0 :(得分:14)

是可能的您可以使用Kint(PHP调试脚本)

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

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

仍然输了?您可以使用它来查看变量内部的内容。

enter image description here

也可以作为debug_backtrace替换器 enter image description here

您可以download here Here

Total Documentations and Help is here

另外,它还支持几乎所有的php框架

  • CodeIgniter
  • Drupal
  • Symfony
  • Symfony 2
  • WordPress
  • Yii
  • 框架
  • Zend Framework

所有最佳......:)

答案 1 :(得分:8)

有三件事情会浮现在脑海中:

  1. 设置IDE,以便逐行调试PHP脚本
  2. 向脚本添加一些日志记录
  3. 在MySQL中查找长时间运行的查询
  4. 调试选项#2是最简单的。由于这是作为一个cron作业运行,你在脚本中添加了一堆echo

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

    然后将stdout传递给cron job命令中的日志文件。

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

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

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

答案 2 :(得分:6)

使用strace从系统角度查看程序基本上是做什么的。是否挂在IO操作等?当遇到任何类型的Linux应用程序的性能问题时,{1}}应该是您尝试的第一件事。没人能躲避它! ;)

如果您发现该程序在与straceconnect和朋友等网络相关的呼叫中挂起,则意味着网络通信在连接或等待响应时会在某个时刻挂起,而不是您可以使用readfrom分析一下。

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

如果上述方法没有帮助,我会使用xdebug对脚本进行分析。您可以使用-p PID

等工具分析探查器输出

答案 3 :(得分:6)

分析工具:

有一个名为Blackfire的PHP分析工具,目前处于公开测试阶段。有关如何profile CLI applications的具体文档。收集配置文件后,您可以在一个漂亮的用户界面中通过时间测量来分析应用程序控制流: Blackfire tool

内存消耗可疑:

  

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

不断增长的内存使用量实际上听起来很可疑!如果当前数据集不依赖于导入的所有以前的数据集,那么增长内存很可能意味着所有导入的数据集都保存在内存中,这很糟糕。 PHP也可能经常尝试垃圾收集,只是为了发现没有什么可以从内存中删除。特别是长时间运行的CLI任务会受到影响,因此请务必阅读发现该行为的blog post

答案 4 :(得分:5)

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

即。抓住一条记录#1,以某种方式攫取它,为它增加价值,重新格式化然后保存它,然后转移到记录#2

我会考虑打破这个大cron。即

Cron#1:获取所有记录,并在本地缓存所有显着数据(到该服务器)。如果达到此阶段,请设置一个标志。

Cron#2:现在你拥有所需的数据,munge并添加值,缓存输出。如果达到此阶段,请设置一个标志。

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

这种“分而治之”将缓解你的调试困境,并让你更好地了解实际发生的事情,并作为奖励让你有机会重新运行说,cron 2。

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

答案 5 :(得分:4)

在过去做网络繁重的工作时,我遇到了奇怪的减速。基本上,我发现在手动测试期间系统速度非常快,但是如果让它无人看管,它就不会像我希望的那样完成。

就我而言,我发现的问题是我有默认的网络超时,许多网络请求只会超时。

通常,虽然不是外部工具,但您可以使用两个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);

为了获得最大的稳健性,方法/函数必须确保它们处理未知标记,缺少参数等。

答案 6 :(得分:4)

xdebug_print_function_stack是一个选项,但您也可以创建一个&#34;函数跟踪&#34;。有三种输出格式。一个是人类可读的跟踪,另一个更适合计算机程序,因为它更容易解析,最后一个使用HTML格式化跟踪

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

答案 7 :(得分:3)

好的,基本上你有两种可能性 - 它是无效的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/

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

P.S。 100条记录的10-20分钟似乎很多。

答案 8 :(得分:2)

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

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

phplapse_start();

然后在下一次迭代中停止:

phplapse_stop();

通过这个过程,当一切看起来都很慢时,你就会创建一个执行快照。

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

答案 9 :(得分:1)

我每天晚上都有类似的事情(一个cron作业来更新我的数据库)。我发现最可靠的调试方法是在数据库中设置日志表并定期插入/更新包含多维数组的json字符串,其中包含有关每条记录的信息以及您想要了解的有关每条记录的任何有用信息。这样,如果您的cron作业没有完成,您仍然可以获得有关它到达的详细信息以及沿途发生的事情。然后你可以编写一个简单的页面来拉出json字符串,将它转回一个数组并将有用的数据打印到页面上,包括计时和通过测试等。当你看到一些问题时,你可以集中精力从中提取更多信息。区域进入json字符串。

答案 10 :(得分:1)

常规&#34;顶部&#34;命令可以显示,如果php或mysql的CPU使用率是瓶颈。如果没有,那么延迟可能是由http呼叫引起的。

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

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