PHP 性能分析第一篇: Xhprof & Xhgui 介绍

原文  http://news.oneapm.com/php-xhprof-xhgui/
主题 XHProf 性能分析

【前言】这是国外知名博主 Davey Shafik所撰写的 PHP 应用性能分析系列的第一篇,阅读第二篇可深入了解 xhgui,第三篇则关注于性能调优实践。

什么是性能分析?

性能分析是衡量应用程序在代码级别的相对性能。性能分析将捕捉的事件包括:CPU的使用,内存的使用,函数的调用时长和次数,以及调用图。性能分析的行为也会影响应用性能。

影响的程度取决于基准测试。基准测试在外部执行,用于衡量应用真实性能。所谓真实性能,即终端用户所体验的应用表现。

什么时候应该进行性能分析?

在考虑是否进行性能分析时,你首先要想:应用是否存在性能问题?如果有,你要进一步考虑:这个问题有多大?

如果你不这样做,将会陷入一个陷阱——过早优化,这可能会浪费你的时间。

为了评断应用是否存在性能问题,你应该确定性能目标。例如,100个并发用户的响应时间小于1s。然后,你需要进行基准测试,看是否达到这个目标。一个常见的错误是,在开发环境进行基准测试。事实上,你必须在生产环境进行基准测试。(实际生产环境或模拟的生产环境,后者很容易在 SaaS 实现(见:OneAPM 性能在线分析)。

用于基准测试的产品很多,包括 ab,siege 和 JMeter。我个人比较喜欢JMeter的功能集,但 ab 和 siege 更加易用。

一旦你确定应用存在性能问题,就需要分析其性能,实施改进,然后再一次进行基准测试,查看问题是否解决。每一次变更之后,你都该进行基准测试查看效果。如果你做了很多变更,却发现应用性能有所下降,你就无法确定具体是哪一次变更导致了这个问题。

下图是我定义的性能生命周期:

性能下降的一般原因

导致性能下降的一般原因中,有些相当出人意料。即便是像 PHP 这样的高级语言,代码的好坏也很少是问题的根源。在当今的硬件配置条件下,CPU 很少是性能限制的原因。常见的原因反而是:

数据存储

  • PostgreSQL
  • MySQL
  • Oracle
  • MSSQL
  • MongoDB
  • Riak
  • Cassandra
  • Memcache
  • CouchDB
  • Redis

外部资源

  • APIs
  • 文件系统
  • 网络接口
  • 外部流程

糟糕的代码

选择哪一种性能分析器?

在 PHP 世界里,有两个截然不同的的性能分析器——主动和被动。

主动 VS 被动性能分析

主动分析器在开发过程中使用,由开发人员启用。主动分析器收集的信息比被动分析器多,对性能的影响更大。通常,主动分析器不能用在生产环境中。Xdebug 就是一种主动分析器。

因为无法在生产环境中使用主动分析器,Facebook 推出了一个被动分析器——XHprof。XHprof 是为了在生产环境中使用而打造的。它对性能的影响最小,同时收集足够的信息用于诊断性能问题。XHprof 和 OneAPM 都是被动分析器。

通常,Xdebug 收集的额外信息对于一般的性能问题分析并不必要。这意味着,被动分析器是用于不间断性能分析的更佳选择,即使是在开发环境中。

Xhprof + Xhgui

Xhprof 由 Facebook 开发的,包含一个基本的用户界面用于查看性能数据。此外,Paul Reinheimer 开发了 Xhgui 和一个增强的用户界面(UI)用于查看、比较和分析性能数据。

安装

安装 XHPROF

Xhprof 可通过 PECL 安装,步骤如下:

$pecl install xhprof-beta

该 pecl 命令将尝试自动更新你的 php.ini 设置。pecl 尝试更新的文件可以使用以下命令找到:

$ pecl config-get php_ini

它会在指定的文件(如果有的话)顶部增加新的配置行。你可能想把他们移到一个更合适的位置。

一旦你编译了该扩展程序,您必须启用它。为此,您需要在 PHP INI 文件添加以下代码:

[xhprof]
extension=xhprof.so

之后,结合 Xhgui 就能轻松地执行性能分析与检查。

安装 XHGUI

安装 Xhgui,必须直接从 git 获取。该项目可以在 github 上找到,地址为https://github.com/perftools/xhgui

Xhgui 要求:

  • PHP 5.3+
  • ext/mongo
  • composer
  • MongoDB(若只需要收集数据,则可选可不选;若需要数据分析,则为必选)

首先,克隆项目到任意位置。在基于 Debian 的 Linux 系统(例如 Ubuntu 等等),可能是 /var/www。在 Mac OS X 系统,可能是 /Library/WebServer/Documents。

$cd /var/www
$ git clone https://github.com/perftools/xhgui.git
$ cd xhgui
$ php install.php

最后一个命令是运行 composer 以安装依赖并检查 xhgui 缓存目录的权限。如果失败,你可以手动运行 composer install。

下一步,你可能需要创建配置文件。这一步很容易实现,可以使用在 /path/to/xhgui/config/config.default.php 下的默认配置文件。

如果你在本地运行 mongodb ,没有身份验证,则可能不需要这样做。因为它将回退为默认值。而在多服务器环境中,你会需要一个所有服务器都能进行存储的远程 mongodb 服务器,并进行恰当的配置。

为提高 MongoDB 的性能,你可以运行以下指令以添加索引:

$ mongo
> use xhprof
db.results.ensureIndex( {'meta.SERVER.REQUEST_TIME': -1} )
db.results.ensureIndex( {'profile.main().wt': -1} )
db.results.ensureIndex( {'profile.main().mu': -1} )
db.results.ensureIndex( {'profile.main().cpu': -1} )
db.results.ensureIndex( {'meta.url':1} )

其他配置

如果你不想在生产环境中安装 mongo ,或无法让 Web 服务器访问 mongo 服务器,您可以将性能分析数据保存在磁盘中,再导入到本地MongoDB 供以后分析。

为此,请在 config.php 中进行以下修改:

<?php
'save.handler' = 'file',
'save.handler.filename' => '/path/to/xhgui/xhprof-' .uniqid("", true). '.dat',
?>

改变文件中的 save.handler,然后取消批注 save.handler.filename ,为其赋一个恰当的值。

注意:默认每天只保存一个分析文件。

一旦分析数据的准备就绪,你就可以使用 xhgui 附带的脚本导入之:

$ php /path/to/xhgui/external/import.php /path/to/file.dat

在此之后的步骤都相同。

运行 Xhgui

Xhgui 是以 PHP 为基础的 Web 应用程序,你可以以 /path/to/xhgui/webroot为根文件,设置一个标准的虚拟主机。

或者,你可以简单地使用 PHP 5.4+ cli-server 例如:

$ cd /path/to/xhgui
$ php -S 0:8080 -t webroot/

这将使 XHGui 在所有网络接口都可通过 8080 端口进行通信。

运行性能分析器

运行分析器时,你需要在待分析的所有页面包含 external/header.php 脚本。为此,你可以在 PHP ini 文件设置 auto_prepend_file 。你既可以直接在公共 INI 文件进行设置,也可以限制到单一的虚拟主机。

对于 Apache 服务器,添加以下代码:

php_admin_value auto_prepend_file "/path/to/xhgui/external/header.php"

对于 Nginx 服务器,在服务器配置中添加以下代码:

fastcgi_param PHP_VALUE "auto_prepend_file=/path/to/xhgui/external/header.php";

如果您使用 PHP 5.4 + cli-server(PHP - S),则 必须 通过命令行标记进行设置:

$ php -S 0:8080 -dauto_prepend_file=/path/to/xhgui/external/header.php

默认情况下,分析器运行时只分析(大约) 1% 的请求。这是由以下 external/header.php 代码控制的:

<?php
if (rand(0, 100) !== 42) {  return;
}
?>

如果你想分析每一个请求(例如,在开发阶段),你可以将这段代码注释掉。如果你想让分析 10% 的请求,你可以做如下改动:

<?php
if (rand(0, 10) !== 4) {return;
}
?>

这允许你对一小部分用户请求进行分析,而不过多影响单个用户或太多用户。

如果你想在性能分析时进行手动控制,你可以这样做:

<?php
if (!isset($_REQUEST['A9v3XUsnKX3aEiNsUDZzV']) && !isset($_COOKIE['A9v3XUsnKX3aEiNsUDZzV'])) {return;
} else {// Remove trace of the special variable from REQUEST_URI$_SERVER['REQUEST_URI'] = str_replace(array('?A9v3XUsnKX3aEiNsUDZzV', '&A9v3XUsnKX3aEiNsUDZzV'), '', $_SERVER['REQUEST_URI']);setcookie('A9v3XUsnKX3aEiNsUDZzV', 1);
}if (isset($_REQUEST['no-A9v3XUsnKX3aEiNsUDZzV'])) {setcookie('A9v3XUsnKX3aEiNsUDZzV', 0, time() - 86400);return;
}
?>

这段代码会检查一个随机命名的 GET/POST/COOKIE 变量(在此例中为:A9v3XUsnKX3aEiNsUDZzV),同时创建一个同名的 Cookie ,用于分析该请求的整个过程,例如:表单提交后的重定向,Ajax 请求等等。

此外,它允许一个名为 no-A9v3XUsnKX3aEiNsUDZzV 的 GET/POST 变量来删除 cookie ,停止分析。

当然,我们欢迎大家尝试使用 OneAPM 来为您的PHP 和Java 应用做免费的性能分析。OneAPM 独有的探针能够深入到所有 PHP 和 Java 应用内部完成应用性能管理和监控,包括代码级别性能问题的可见性、性能瓶颈的快速识别与追溯、真实用户体验监控、服务器监控和端到端的应用性能管理。 OneAPM 可以追溯到性能表现差的 SQL 语句 Traces 记录、性能表现差的第三方 API、Web 服务、Cache 等等。

在下一篇文章中,我们将深入研究 Xhgui ,以及用于展示、比较 xhprof 数据的用户界面( 本文系应用性能管理领军企业OneAPM 工程师编译整理 ) 。

PHP 性能分析第二篇: Xhgui In-Depth

原文  http://news.oneapm.com/profiling-php-part-2-xhgui-in-depth/
主题 PHP 性能分析

【前言】这是国外知名博主 Davey Shafik 撰写的 PHP 应用性能分析系列的第二篇,第一篇介绍Xhprof/Xhgui,第三篇则关注于性能调优实践。

在第一篇中,我们初步介绍了 xhprof,以及如何安装和运行分析器。在本文,我们将介绍 Xhgui——用于审查并比较 xhprof 数据的用户界面(UI)。

使用 Xhgui

Xhgui 提供了许多协助性能评估的功能,既适用于单次运行,也能满足聚合环境——让你精确至具体问题、发现趋势。

术语

为了提高 Xhgui 的使用效率,你需要熟悉许多术语:

1.调用次数

函数调用的次数

2.[包含] 实际执行时间 (wt)

函数实际执行时间

3.[包含] CPU 使用/CPU 用时 (cpu)

运行该函数 CPU 所用时间

4.[包含] 内存使用 (mu)

目前该函数使用的内存量

5.[包含] 内存使用量峰值 (pmu)

函数使用的内存高峰

6.专一实际执行时间 (ewt)

7.专一 CPU 时间 (ecpu)

8.专一内存使用量 (emu)

9.专一内存使用量峰值 (epmu)

术语2至5都是包含型的测量指标(尽管不总是明确指出),这些指标会计算函数及其子函数的调用。术语6至9是专一型的测量指标——它们只计算函数本身的资源调用。所有的测量数值都是调用该函数后的累计值。(例如,如果一个函数调用两次,第一次用时900毫秒,第二次,因为缓存的缘故,只耗时40毫秒,最终显示的时间就是940毫秒)。

准备开始

一旦在 HTTP 服务器上运行 Xhgui ,你首先会看到:

在顶部,你会看到一个菜单,它包含:

Recent — 近期大部分运行 (分页)

Longest wall time — 根据实际执行时间从最慢的运行开始排序

Most CPU — 从占用 CPU 时间最多的运行开始排序

Most Memory — 从占用内存最多的运行开始排序

Custom View — 执行 mongo DB 自定义查询

Watch Functions — 应该出现在审查页面顶部的标记函数

Waterfall — 从实验性视图查看并发请求的相互影响

在本教程中,我选择分析用 Wordpress 搭建的网站性能。互联网上多于18%的网站都是基于 Wordpress 搭建的,这意味着,即便是对 Wordpress 很小的性能改进, 亦能产生巨大影响。

查看一次运行的性能数据

分析了几个页面的性能(或导入了文件)之后,你会看到它们罗列在 Xhgui :

查看一次运行的性能数据,只需点击日期。

通过单击适当的表头,你可以根据实际执行时间 (wt) , CPU 时间 (CPU) 、内存使用量 (mu) 或 内存使用量峰值 (pmu) 查看这些运行情况。从而轻易找出最慢的页面。

单个性能页面展示了相当多的信息。在左侧可以看到运行的总体情况,以及运行时的环境数据,包括 GET (或 POST) 数据和服务器数据:

在右侧,展示了 watch function 列表:

该表详细列出了函数名称,调用次数 ,专一实际执行时间 (ewt), 专一内存使用量(emu)、和专一内存使用峰值(epmu)。此外,你可能会注意到页面顶部的两个按钮,“View Callgraph(查看调用图)” 和 “Compare this run(对比此次运行)”。

接下来,我们看到两个图。图一展示了专一实际执行时间最长的六个函数,该时间是用在函数本身的时间(不包含任何子函数调用所占的时间)。图二展示内存使用量最大的六个函数。这些图通常能将你指向性能瓶颈。

函数的细节在下方列出。如果将鼠标滑过图中的圆柱,这些信息也将出现在提示框中。

最后,我们看到性能分析器收集到的大宗信息——函数列表:

该表包含一个浮动的标题栏(即便鼠标向下滚动,该栏目也会保持在屏幕顶端),包含函数名,调用次数,和前面提到的专一和包含的测量值。

默认情况下,该表按专一实际执行时间排序,时间最长者排在首位。通常你不会想改变这一次序,因为这让你快速找出运行最慢的函数,除非你想看内存使用量。

当你想查看一个函数的运行情况时,点击该函数,会跳转到其详细页面。该页面首先会递归展示函数本身的细节。接下来, “Parent Functions(父函数)” 部分列出所有直接调用该函数的函数。最后,“Child Functions(子函数)”列出该函数直接调用的其他函数。

父函数按照专一实际运行时间,列出标准列表数据。

你需要确定:是函数本身运行缓慢,还是调用它的次数太多导致累积的实际执行时间太长。通过检查该函数的调用计数,然后回顾其父函数列表。

如果你觉得函数调用次数没问题,你就要看看子函数运行情况。此处才是函数运行消耗时间的部分。

子函数只显示包含测量值;这是因为你想很快找到耗时最长的代码路径。

你可以点击每个子函数,下钻到相同的细节视图,并进行相同的分析。

比较性能数据

Xhgui 最好的特性在于比较两个不同的运行。这使你:

  • 修改系统 (如启用 opcache , mysql 查询缓存) 并比较结果
  • 修改代码(代码或 SQL 优化)并比较结果
  • 将异常的运行与“正常”运行比较

比较两个运行时,你必须首先选择一个基础运行。点击其日期就能看到该运行的详细信息页。

接下来,单击右上角的“Compare this run” 按钮:

接着会跳转到同一 URL 下的运行列表,你可以选择一个进行比较:

点击你想进行比较的运行的 "Compare" 按钮,将跳转到比较页面。

比较视图只显示两个运行之间的差异。在页面顶部显示比较中的两个运行,以及一些辅助修改排序的按钮。

接下来是概览:

尽管这个表的所有信息都有用,但特别值得注意的两个差别是 "函数调用次数" 和 "专一实际运行时间" 。

函数调用次数的差别暗示着两次运行的重要差异:不同的代码路径或缓存。第一个差别可能是有意的优化导致的,但若这并非你的目的,比较这两个运行很可能不会有太大的价值。另一方面,缓存是有益且有效的提高性能的方式。这种比较很容易验证缓存是否发生。

包含实际执行时间的百分比差展示了性能调优的实际成果。理想情况下,我们将看到一个较小的百分比——这是第二运行时间比上第一次运行时间的占比。在截图中,第二次运行只花了第一次运行79%的时间,这意味着性能提升了21%。

最后,我们看到功能细节:

请记住,该视图只展示差别。差别通过绿色的负数和红色的正数表示。(负数表明调用次数更少,实际执行时间更短,CPU 耗时更短或内存消耗更少)如果没有差异,则显示为灰色的0。

与其他表一样,您可以在任意列进行排序,默认的顺序方式是函数的调用次序。

在这里你可以验证,你做的改变是否确有效果,是否为预期效果。你也可以在性能下降时使用该视图追踪原因。

性能提高的一个好例子是:基于一个条件只调用一个函数——例如,您可能不需要过滤数据,如果之前已经做了。

当你做出这种改变时,你会预期过滤函数的调用次数减少,从而性能提升。

这两件事都可以在此处得到验证,以及其他意想不到的原因——你的条件比过滤本身需要更长的时间?如果真是如此,这将对性能产生负面影响。

在此处,我们可以看到, NOOP Translations::translate 和 apply filter 的调用次数都减少了,但是 apply_filter 的专一内存使用量增加了133560个字节!

发现趋势

对我来说,Xhgui 最强大的功能是查看趋势。因为 xhprof 是被动分析器,可以在所有环境中启用 (dev、qa、阶段性、生产),可以持续地对流量取样分析。

审查给定 URL 的所有数据,只需在运行列表点击它:

这将跳转到该 URL 运行页面。

该页面显示两个重要图表。第一个显示实际运行时间和 CPU 时间,第二个显示内存使用情况和峰值内存使用。这些图表列表中运行的数据,包括每次运行的 URL,时间,实际运行时间、CPU时间、内存使用和峰值。

这些图是查看趋势和异常值的关键所在。但是该如何处理这些信息呢?

对于数据异常者,首先你可以将鼠标悬浮在它上面验明正身,接着,你可以看一下它的单次运行。或用其他正常运行与其比较,从而发现不同。

对于趋势,最好的选择是审查趋势开始的时间——你在此时添加缓存了吗?随着缓存变得更加完整,整体趋势应该向下。或者你的缓存失效,你将看到一个上升趋势,此时缓存正在重建。

默认情况下,这些图表显示最近100次运行,你可以点击下一页去查看更久远时间的运行。

另外,你可以点击搜索按钮来定制显示的界面:

单击该按钮将显示搜索表单:

你可以搜索具体日期之间的运行。也可以查看最近30分钟、1小时、2小时、12小时、24小时、1周、2周或30天内的运行——更小的时间间隔适合评估性能调优的结果。

最后,你可以使用 PHPs DateTimeIntervalInterval 规范格式 指定自定义时间区间——例如,最近2天可使用 P2D,最近15分钟可使用 PT15M。

Watch Functions

Watch functions 允许你通过正则表达式识别特定的函数,或函数组,并显示在单个运行页面(见前文)。

因为可以使用正则表达式,我们可以轻易地查看一个模块或扩展中的功能。

For example, to watch all MySQL activity, simply add one of the following:例如,查看所有 MySQL 活动,只需添加如下列表的任意一项:

  • mysql_(.*)for ext/mysql
  • mysqli(.*)for ext/mysqli
  • pdo(.*)for PDO (适用于所有PDO-based数据库交互)

如果你使用诸如 Propel 的 ORM,你可能使用 (. )Query::(. ) 追踪所有 Query 类。

调用图(Callgraphs)

Xhprof 的最后一部分是调用图 ,该图展示运行的代码执行路径。

点击单一运行页面顶部的“View Callgraph”按钮即可查看调用图。

在调用图中,拖拽结点可以更好地查看数据。鼠标悬浮在每个点击上,会显示其包含实际执行时间,同时允许你进入该函数的详情页。

更直观地查看图,请点击: 体验免费使用OneAPM在线PHP应用性能分析SaaS服务!

下一章节

在第三部分也即最后一部分,我们会使用 xhprof 数据来优化代码。我们也会简单地介绍其他优化代码的工具。 (本文系应用性能管理领军企业OneAPM 工程师编译整理)

PHP 性能分析第三篇: 性能调优实战

原文  http://www.ituring.com.cn/article/205844
主题 PHP 性能分析 数据库

注意:本文是我们的 PHP 性能分析系列的第三篇,点此阅读 PHP 性能分析第一篇: XHProf & XHGui 介绍 ,或  PHP 性能分析第二篇: 深入研究 XHGui 。

在本系列的 第一篇 中,我们介绍了 XHProf 。而在 第二篇 中,我们深入研究了 XHGui UI, 现在最后一篇,让我们把 XHProf /XHGui 的知识用到工作中!

性能调优

不用运行的代码才是绝好的代码。其他只是好的代码。所以,性能调优时,最好的选择是首先确保运行尽可能少的代码。

OpCode 缓存

首先,最快且最简单的选择是启用 OpCode 缓存。OpCode 缓存的更多信息可以在这里 找到。

在上图,我们看到启用 Zend OpCache 后发生的情况。最后一行是我们的基准,也即没有启用缓存的情况。

在中间行,我们看到较小的性能提升,以及内存使用量的大幅减少。小的性能提升(很可能)来自 Zend OpCache 优化,而非 OpCode 缓存。

第一行是优化和 OpCode 缓存后结果,我们看到很大的性能提升。

现在,我们看看 APC 之前和之后的变化。如上图所示,跟 Zend OpCache 相比,随着缓存的建立,我们看到初始(中间行)请求的性能下降,在消耗时长与内存使用量方面的表现都明显下降。

接着,随之 opcode 缓存的建立,我们看到类似的性能提升。

内容缓存

第二件我们能做的事是缓存内容——这对 WordPress 而言小菜一碟。它提供了许多安装简便的插件来实现内容缓存,包括 WP Super Cache。WP Super Cache 会创建网站的静态版本。该版本会在出现诸如评论事件时依照网站设置自动过期。(例如,在非常高负载情况下,您可能会想禁止任何原因造成的缓存过期)。

内容缓存只能在几乎没有写操作时有效运行,写操作会使缓存失效,而读操作不会。

你也应该缓存应用从第三方 API 处收到的内容,从而减少由于 API 可用性导致的延迟与依赖。   WordPress 有两个缓存插件,可以大大提高网站的性能: W3 Total Cache 和  WP Super Cache 。

这两个插件都会创建网站的静态 HTML 副本,而不是每次收到请求时再生成页面,从而压缩响应时间。

如果你正在开发自己的应用程序,大多数框架都有缓存模块:

  • Zend Framework 2: Zend\Cache
  • Symfony 2: Multiple options
  • Laravel 4: Laravel Cache
  • ThinkPHP 3.2.3: ThinkPHP  Cache

查询缓存

另一个缓存选项是查询缓存。针对 MySQL,有一个通用的查询缓存帮助极大。对于其他数据库,将查询结果集缓存在 Memcached 或者 cassandra 这样的内存缓存,也非常有效。

跟内容缓存一样,查询缓存在包含大量读取操作的场景是最有效的。由于少量的数据改动就会使大块的缓存区无效,尤其不能在这种情况下依赖 MySQL 查询缓存来提高性能。

查询缓存或许在生成内容缓存时对性能有提升。

如下图所示,当我们开启查询缓存后,实际运行时间减少了 40% ,尽管内存使用量没有明显改变。

现有三种类型的缓存选项,由 query_cache_type 控制设置。

  • 设置值为 0 或 OFF 将禁用缓存
  • 设置值为 1 或 ON 将缓存除了以 SELECT SQL_NO_CACHE 开头之外的所有选择
  • 设置值为 2 或 DEMAND 只会缓存以 SELECT SQL_CACHE 开头的选择

此外,你应该将 query_cache_size 设置为非零值。将它设置为零将禁用缓存,不管 query_cache_type 是否设置。

想得到设置缓存的帮助,与许多其他性能相关的设置,请查看 mysql-tuning-primer脚本。

MySQL 查询缓存的主要问题是,它是全局的。对缓存结果集构成的表格的任何更改都将导致缓存失效。在写入操作频繁的应用程序中,这将使缓存几乎无效。

然而,你还有许多其他选择,可以根据你的需求和数据集建立更多的智能缓存,例如 Memcached ,  riak ,  cassandra 或  redis

查询优化

如前所述 ,数据库查询常常是程序执行缓慢的原因,查询优化往往能比代码优化带来更多切身的好处。

查询优化有助于生成内容缓存时提高性能,而且,在无法缓存这种最坏的情况下也有益处。

除了分析, MySQL 还有一个帮助识别慢查询的选择——慢查询日志。慢查询日志会记录所有耗时超过指定时间的查询,以及不使用索引的查询(后者为可选项)。

您可以在 my.cnf 中使用以下配置启用日志。

[mysqld]
log_slow_queries =/var/log/mysql/mysql-slow.log
long_query_time =1
log-queries-not-using-indexes

任何查询如果慢于 long_query_time (以秒为单位),该查询就会记录到日志文件log_slow_queries 中。默认值是10秒,最低1秒。

此外, log-queries-not-using-indexes 选项可以将任何不使用索引的查询捕获到日志中。

之后我们可以用与 MySQL 捆绑在一起的 mysqldumpslow 命令检查日志。

在 WordPress 安装时使用这些选项 ,主页加载完成并运行后得到如下数据:

$ mysqldumpslow -g "wp_" /var/log/mysql/mysql-slow.logReading mysql slow query log from /var/log/mysql/mysql-slow.logCount: 1  Time=0.00s (0s) Lock=0.00s (0s) Rows=358.0(358), user[user]@[host] SELECT option\_name, option\_value FROM wp_options WHERE autoload ='S'Count: 1 Time=0.00s (0s) Lock=0.00s (0s) Rows=41.0(41), user[user]@[host] SELECT user\_id, meta_key, meta_value FROM wp_usermeta WHERE user_id IN (N)

首先,注意所有字符串值都以 S 表示,数字则以 N 表示。你可以添加 -a 标志来显示这些值。

接下来,请注意,这两个查询均耗时 0.00 s,这意味着他们的耗时在 1 秒的阈值以下,且没有使用索引。

在 MySQL 控制台 使用 EXPLAIN,可以检查性能下降的原因:

    mysql> EXPLAIN SELECT option_name, option_value FROM wp_options WHERE autoload = 'S'\G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: wp_optionstype: ALLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 433Extra: Using where

此处,我们看到 possible_keys 是 NULL,从而确认未使用索引。

EXPLAIN是对优化 MySQL 查询非常强大的工具,更多信息可以在  这里 找到。

PostgreSQL 同样也包括一个 EXPLAIN (该 EXPLAIN 与 MySQL 的差别很大),而 MongoDB 有 $explain 元 操作符 。

代码优化

通常只有当你不再受到 PHP 本身限制(通过使用 OpCode 缓存),缓存了尽可能多的内容,优化了查询之后,才可以开始调整代码。

代码和查询优化带来足够的性能提升才能创建其他缓存;代码在最糟糕的环境(没有缓存)下性能越高,应用就越稳定,重建缓存的速度也就越快。

让我们看看如何(潜在地)优化我们的 WordPress 安装。

首先,让我们看看最慢的函数:

令我惊讶的是,列表中的第一项 不是 MySQL (事实上 mysql_query() 是第四),而是 apply_filter() 函数。

WordPress 代码库的特点是,通过基于事件的过滤系统执行多种数据转换,执行次序按照数据经内核、插件添加或回调的顺序。

apply_filter()函数是这些回调应用的地方。

首先,你可能会注意到,函数被调用 4194 次。如果我们点击查看更多细节,就可以按照“调用次数”降序排列“父函数”,从而发现 translate() 调用了 apply_filter() 函数 778 次。

这很有趣,因为实际上我不使用任何翻译。我(并怀疑大多数用户)在使用 WordPress 软件时都设置为本土语言:英语。

因此,让我们点击查看细节,进一步查看该 translate() 函数在做什么。

在这里,我们看到两间有趣的事。首先,在父函数中,有一个被调用了773次:__()。

查看该函数的源代码后,我们发现它是 translate() 的包装器。

    <?php/*** Retrieves the translation of $text. If there is no translation, or* the domain isn't loaded, the original text is returned.** @see translate() An alias of translate()* @since 2.1.0** @param string $text Text to translate* @param string $domain Optional. Domain to retrieve the translated text* @return string Translated text*/function __( $text, $domain = 'default' ) {return translate( $text, $domain );}?>

根据经验法则,函数调用代价昂贵,应该尽量避免。现在我们总是调用 __() 而不是translate() ,我们应该把别名改为 translate() 来保持向后兼容性,而 __() 则不再调用非必要的函数。

然而,实际上,这种改变不会带来多大的差异,只是微观的优化罢了——但它的确提高了代码可读性,简化了调用图。

继续前进,让我们看看子函数:

现在,深入该函数,我们看到有 3 个 函数或方法被调用,每个 778 次:

  • get_translations_for_domain()
  • NOOP_Translations::translate()
  • apply_filters()

按照包容性实际运行时间降序排列,我们看到 apply_filter() 是目前为止耗时最长的调用。

查看代码:

    <?phpfunction translate( $text, $domain = 'default' ) {$translations = get_translations_for_domain( $domain );return apply_filters( 'gettext', $translations->translate( $text ), $text, $domain );}?>

这段代码的作用是检索一个翻译对象,然后将 $translations->translate() 的结果传给apply_filter() 。我们发现 $translations 是 NOOP_Translations 类的一个实例。

仅根据名称( NOOP ),再经代码中的注释证实,我们发现翻译器实际上没有任何动作!

    <?php/*** Provides the same interface as Translations, but doesn't do anything*/class NOOP_Translations {?>

因此,也许我们完全可以避免这种代码!

通过在代码上进行小规模调试,我们看到当前使用的是默认的域,我们可以修改代码以忽略翻译器:

    <?phpfunction translate( $text, $domain = 'default' ) {  if ($domain == 'default') {
      return apply_filters( 'gettext', $text, $text, $domain );
  }
  $translations = get_translations_for_domain( $domain );
  return apply_filters( 'gettext', $translations->translate( $text ), $text, $domain );}?>

接下来,我们再次分析,确保要运行 至少两次 ——确保所有缓存都建立,才是公平的对比!

这次运行的确更快!但是,快多少?为什么?

使用 XHGui 的比较运行这一特性就能找到答案。回到我们最初的运行,点击右上角的 “比较此处运行” 按钮,并从列表中选择新的运行。

我们发现,函数调用的次数减少了3% ,包容性实际运行时间减少 9% ,包容性CPU时间减少12%!

之后,可以按调用次数降序排列细节页,这证实(如同我们的预期)get_translations_for_domain() 和 NOOP_Translations::translate() 函数的调用次数减少。同样,可以确认没有预料之外的变化发生。

30 分钟的工作带来9 - 12% 的性能提升,这非常可喜。这就意味着真实世界的性能收益,即便是在应用了 opcache 之后。

现在我们可以对其函数重复这个过程,直到找不到更多优化点。

注意:此更改已提交到 WordPress.org 并已获更新。你可以在  WordPress Bug Tracker 跟踪讨论,查看实践过程。此更新计划包含在 WordPress 4.1 版本中。

其他工具

除了出色的 XHProf/XHGui,还有一些很好的工具。

New Relic & OneAPM

New Relic 与  OneAPM 均提供前后端性能分析;洞察后台堆栈讯息,包括 SQL 查询与代码分析,前端 DOM 与 CSS 呈现,以及 Javascript 语句。 OneAPM 更多功能请移步 ( OneAPM 在线DEMO )

uprofiler

uprofiler 是目前还未发布的 Facebook XHProf 分支,该分支计划删除 Facebook 所需的 CLA。目前,两者具备相同的特性,只有一些部分重命名了。

XHProf.io

XHProf.io 是 XHProf 的另一种用户界面。XHProf.io 在配置文件存储使用 MySQL ,用户友好性方面不及 XHGui。

Xdebug

在 XHProf 出现之前, Xdebug 早已存在——Xdebug 是一种主动的性能分析器,这意味着它不应该用于生产环境,但可以深入了解代码。

然而,它必须与另一个工具配合使用以读取分析器的输出 , 比如 KCachegrind。但是 KCachegrind 很难安装在非 linux 机器上。另一个选择是 Webgrind 。

Webgrind 无法提供 KCachegrind 的那些特性,但它是一个 PHP Web 应用程序,在任何环境都易于安装。

若搭配 KCachegrind ,你可以轻易探索并发现性能问题。(事实上,这是我最喜欢的剖析工具!)

结语

分析和性能调优是非常复杂的工程。有了对的工具,并理解如何善用这些工具,我们可以很大程度地提高代码质量——即使是对我们不熟悉的代码库。

花时间去探索和学习这些工具是绝对值得的。

注意:本文是我们的 PHP 性能分析系列的第三篇,阅读 PHP 性能分析第一篇: XHProf & XHGui 介绍 ,和   PHP 性能分析第二篇: 深入研究 XHGui 。( 本文系 应用性能管理 领军企业 OneAPM 工程师编译整理 )

[转]PHP 应用性能分析 - Davey Shafik(全)相关推荐

  1. 火焰图 性能分析 java,使用火焰图进行Java应用性能分析

    作者: 一字马胡 转载标志 [2017-11-19] 更新日志 日期 更新内容 备注 2017-11-19 新建文章 初版 导入 本文主要想要记录进行java应用性能分析的一种方式,也就是使用火焰图来 ...

  2. Web应用性能分析工具—HAR文件

    Web应用性能分析工具-HAR文件 来源 https://raynorli.com/2018/06/11/web-performance-analysis-har-file/ 客户经常有的一个问题就是 ...

  3. Java应用性能分析工具:async-profiler(配合FlameGraph生成火焰图)

    前言: 及时对项目或者服务器Java应用性能进行性能检测,并且分析检测结果数据,发现热点代码是一项充满意义的工作,因为可能因为某一段热点代码会拖慢整个系统的运行,这是不可忍受的,发现热点代码之后需要及 ...

  4. Java应用性能分析工具:async-profiler

    https://www.jianshu.com/p/9364028cca4e 厉害的内容 及时对项目进行性能检测,并且分析检测结果数据,发现热点代码是一项充满意义的工作,因为可能因为某一段热点代码会拖 ...

  5. 实战|智能家居行业移动应用性能分析

    随着移动互联网和物联网技术的成熟,智能家居再度成为全球制造业转型升级的焦点,传统制造业巨头和新兴互联网巨头纷纷布局,据调研机构statista数据显示,截至2016年10月,全球智能家居市场收入为16 ...

  6. 微服务应用性能分析实战14 互通有无:如何设计跨语言的 APM 交互协议?

    开源的 APM 产品的跨语言交互协议,以 2010 年 Google 发布 Dapper 论文的时间为节点,切分为两个阶段. 论文发布前,大多数跨语言交互协议的设计能简单解决上下游两个应用无法串联的问 ...

  7. 有了这款可视化工具,Java 应用性能分析、调优 so easy...

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者: 朝雨忆轻尘 cnblogs.com/xifengxiaom ...

  8. 笔记:Java 性能优化权威指南 第6章 Java 应用性能分析技巧

    一.性能优化的类型 1.使用更高效的算法 2.减少锁竞争 3.为算法生成更有效率的代码 二.CPU的使用 用BufferedOutputStream 替代直接的 FileOutputStream 利用 ...

  9. java profiler 工具_(转)Java应用性能分析工具:async-profiler

    环境准备 首先,你需要从github将代码下载下来: git clone https://github.com/jvm-profiling-tools/async-profiler 然后,进入到下载好 ...

最新文章

  1. 使用LocalDate计算给定2个日期的几年几月几日
  2. 列表自定义的Type和BaseType参考
  3. 如何用课件制作工具演示面积一定的矩形
  4. Unity屏幕射线碰撞
  5. 今晚8点:基于强化学习的关系抽取和文本分类 | PhD Talk #18
  6. 【Java语法】StringBuilder 可变字符序列 和普通String的对比
  7. jQuery选择器之层级选择器
  8. Java SE 8新功能介绍:Lambda的遍历,过滤,处理集合和方法增强
  9. Kung Fu Panda: Legends of Awesomeness 2
  10. redis-Set集合操作SADD,SMEMBERS,scard,srem
  11. 剑灵火龙区服务器位置,剑灵双线火龙新区活动介绍
  12. 在C#中什么时候用分号?
  13. DeepMatch交友机器人:原来姹紫嫣红开遍,缘来就是你
  14. Flash builder 4.7 离线安装svn
  15. 学计算机二级的免费软件,计算机二级MS模拟软件
  16. BS4爬取物价局房产备案价以及dataframe的操作来获取房价的信息分析
  17. Python实现邮箱自动群发工资条
  18. MATLAB余弦相似性学习
  19. 三言二拍:五年有多长对牛乱弹琴 | Playinamp;#39; with IT
  20. 从滚动条到画布的几个代码文件——Python学习笔记之十七

热门文章

  1. java实现简单的orm_一种利用反射自动封装JavaBean的想法的实现(简单的ORM,较实用于JDBC查询)...
  2. XP系统“Windows无法配置此无线连接”的解决办法
  3. Android自动化测试之Monkey命令使用及monkey脚本编写
  4. Java 线程线程状态流程图
  5. input输入框onkeydown事件:键入值监控
  6. 弘辽科技:为什么要设置淘金币?淘金币该如何设置?
  7. deepin linux 安装最新版node方法
  8. Jmeter 证书导入
  9. ManageEngine ADManager Plus 监控
  10. yshon对讲机如何调频率_对讲机频率怎么调