【问题标题】:debugging long running PHP script调试长时间运行的 PHP 脚本
【发布时间】:2017-01-22 23:36:01
【问题描述】:

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

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

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

所以,我需要对其进行调试以找到瓶颈。脚本、底层代码库、php 本身、数据库、操作系统或网络部分可能有问题。我现在怀疑某处的某种缓存表现不佳,未命中率接近 100%。

我无法使用 XDebug,配置文件增长太快而无法处理。

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

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

【问题讨论】:

  • 绝对没有能力创建一个开发环境,以便您可以对每个部分进行计时并计算运行 1 条记录需要多长时间?另外,你一天中的什么时候运行这个?是从中午开始还是半夜开始?

标签: php linux debugging


【解决方案1】:

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

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

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

还是迷路了?您可以使用它来查看变量内部的内容。

也可以充当 debug_backtrace 替代者

你可以download hereHere

Total Documentations and Help is here

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

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

一切顺利...... :)

【讨论】:

    【解决方案2】:

    我想到了三件事:

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

    调试选项 #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 作业命令中的日志文件。

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

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

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

    【讨论】:

      【解决方案3】:

      分析工具:

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

      内存消耗可疑:

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

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

      【讨论】:

      • 嗯,这很有趣。我已经测试了短期运行(1600 条记录的 300 条记录),它花费的时间更少(使用 gc 的时间为 398 秒,而没有 gc 的时间为 339 秒),但内存使用量猛增:使用 gc 时为 112 M,而没有 gc 时为 572 M。所以似乎没有 gc 的选项是不行的 - 我不能允许脚本在 2G 服务器上使用 2.5 G 的内存。
      • 由于关闭 GC 时内存使用量猛增,开启时 GC 似乎正在释放内存,这是一个好兆头 - 尽管没有帮助。作为下一步,我建议使用 Blackfire 分析器来查明脚本中的可疑部分。 Blackfire 团队有一个很好的blog post,解释了他们如何使用 Blackfire 来分析 CLI 工具的内存/CPU 使用情况。还是您无法在服务器上使用 Blackfire?
      【解决方案4】:

      使用strace 从系统的角度来看程序基本上在做什么。它是否挂在 IO 操作等中? strace 应该是您在遇到任何类型的 Linux 应用程序的性能问题时尝试的第一件事。谁也躲不过! ;)

      如果您发现程序在网络相关的调用(如 connectreadfrom 和朋友)中挂起,这意味着网络通信在连接或等待响应时确实挂起,而不是您可以使用 tcpdump 来分析一下。

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


      如果上述方法没有帮助,我将使用xdebug 分析脚本。您可以使用KCachegrind等工具分析分析器输出

      【讨论】:

        【解决方案5】:

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

        即获取记录#1,以某种方式对其进行处理,为其添加价值,重新格式化然后保存,然后移动到记录#2

        我会考虑分解大 cron。即

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

        Cron #2:现在你有了你需要的数据,处理和增加价值,缓存那个输出。如果达到此阶段,则设置一个标志。

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

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

        我不得不这样做很多次,对我来说,日志记录是识别代码中的弱点、识别对数据质量的不良假设以及暗示延迟导致问题的关键。

        【讨论】:

          【解决方案6】:

          我过去在进行大量网络工作时遇到了奇怪的减速。基本上,我发现在手动测试期间系统非常快,但在无人看管的情况下运行时,它不会像我希望的那样完成。

          在我的情况下,我发现的问题是我设置了默认网络超时,并且许多 Web 请求只会超时。

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

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

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              【解决方案8】:

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

              这些工具非常强大且用途广泛。

              附: 100 条记录需要 10-20 分钟。

              【讨论】:

              • 但是 php 慢日志不会随着 xdebug 配置文件增长而完全无法使用吗? PS。大多数记录扩展为数百个子记录,可能有 HTTP 请求来收集外部信息;但它很多,我同意。
              • 它将拥有所有慢查询的有用信息。由你来分析。当然,为了防止它过度增长 - 应该设置正确的日志轮换。
              【解决方案9】:

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

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

              phplapse_start();
              

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

              phplapse_stop();
              

              通过这个过程,当一切似乎都运行缓慢时,您创建了一个执行快照。

              (我是项目的作者,请随时与我联系以改进功能)

              【讨论】:

                【解决方案10】:

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

                【讨论】:

                  【解决方案11】:

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

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

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2019-03-26
                    • 1970-01-01
                    • 2019-02-12
                    • 1970-01-01
                    • 2016-11-14
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多