PHP 应用性能优化指南

PHP7
程序员都喜欢最新的 PHP 7,因为它使 PHP 成为执行最快的脚本语言之一(参考 PHP 7 vs HHVM 比较)。但是保持最佳性能不仅需要快速执行代码,更需要我们知道影响性能的问题点,以及这些问题的解决方案。本文涵盖了保障 PHP 应用平稳高速运行的所有知识点,大量干货来袭,强烈建议收藏。

PHP 简史#

PHP 是由拉斯姆斯・勒多夫于 1995 年开始开发的。起初,它只是勒多夫为了要维护个人网页,而用 c 语言开发的一些 CGI 工具程序集,我们从 PHP 这个缩写最初的来源 “Personal Home Page”(个人主页)就可以看出这一点。然而,随着勒多夫不断地扩充它的功能,PHP 逐渐成为了现在的 “PHP:超文本预处理器”。

在过去的 20 年中,PHP 的开发团队一直致力于提升 PHP 的性能,最引人瞩目的是于 1999 年引入的 Zend 语法解释器引擎。2000 年发布的 PHP 4,包含了一个內建的编译器和执行器模型,使得 PHP 开始有能力开发动态的 Web 应用。2015 年 PHP 发布了里程碑式的版本 PHP 7.0,极大的提升了 Zend 引擎的性能,并降低了 PHP 的整体内存使用率。截止到本文发稿为止,目前最新的 PHP 版本是 7.1.4,有兴趣的话可以看看这篇文章 PHP7 新特性,改变变化

怎样才算是高性能的 PHP 应用?#

性能和速度不是一对同义词。实现最佳性能通常需要在速度、准确性和可扩展性之间进行权衡。例如,在开发 Web 应用时,如果你优先考虑速度,你可能会编写一个将所有内容都载入内存的脚本,而如果从可扩展性出发,可能你就会编写以块为单位将数据载入内存的脚本。

基于 phpLens 的研究,下图展示了速度与可扩展性之间理论上的权衡关系。

Performance

红线表示针对速度进行了优化的脚本,蓝线是可扩展性优先的脚本。当并发连接数低时,红线运行速度更快; 然而,随着并发连接数量的增加,红线变慢。当并发连接数上升时,蓝线也减慢;然而,下降并不那么剧烈,因此,在一定阈值后,速度优先的脚本会比可扩展性优先的脚本慢。然而,在现实当中,一些脚本可能随着运行环境的变化而表现出前后不同的性能差异。你需要仔细的观察用户的使用情况,以及应用的并发请求数量,来适时调整合适的优化策略。

PHP 代码优化最佳实践#

编写好的 PHP 代码是创建快速稳定 Web 应用的关键一步。从一开始就遵循一些最佳实践技巧将节省后期填坑的时间。

1. 尽可能的使用 PHP 的内置方法#

只要可以尽可能的使用 PHP 的内置方法,而不是自己编写相同功能的方法。花点时间去熟悉和学习 PHP 的内置方法,不但可以帮助你更快的编写代码,而且可以使你编写的代码更高效的运行。

2. 使用 Json 替代 xml#

json_encode()json_decode() 等 PHP 的内置方法,运行速度都非常快,所有应该优先使用 Json。如果你无法避免使用 xml,那么请务必使用正则表达式而不是 DOM 操作来进行解析。

3. 使用缓存技术#

Memcache 特别适用于减少数据库负载,而像 APCOPcache 这样的字节码缓存引擎在脚本编译时可节省执行时间。

4. 减少不必要的计算#

当一个变量会被多次使用时,一开始就计算好,肯定要比每次使用时都计算一遍要更高效。

5. 使用 isset () 和 empty ()#

与 count ()、strlen () 和 sizeof () 函数相比,isset()empty() 对于检测一个变量是否为空等场景更加简单和高效。

6. 减少不必要的类#

如果你不打算重复使用一个类或者方法,那么它就没什么存在的价值。而如果你必须要定义和使用一个类,则需要合理规划类中的方法,对于不是特别公用的方法,尽量将他们放到子类中去,因为调用子类中的方法,比调用父类方法速度更快。

7. 在生产环境关闭用作调试的相关代码及错误报告#

开发时打开错误报告,可以让你避免很多潜藏的 Bug,而一些调试代码也有助于你定位 Bug,但是当代码部署到生产环境后,这些错误报告和调试代码会拖慢你的程序速度,而且将一些错误报告直接显示给用户,也具有相当的安全风险。因此,在生产环境请关闭它们。

8. 关闭数据库连接#

当使用完毕后,注销变量和关闭数据库连接,可以释放珍贵的内存资源。

9. 使用聚合函数减少数据库查询#

查询数据库时,使用聚合函数,可以减少检索数据库的频率,并且使程序运行的更快。

10. 使用强大的字符串操作函数#

举个例子,str_replace () 比 preg_replace () 要快,而 strtr() 函数则比 str_replace () 函数快四倍。

11. 尽量使用单引号#

如果可能,尽量使用单引号替代双引号。程序运行时,会检查双引号中的变量,这会拖慢程序的性能。

12. 尝试使用恒等运算符#

由于 “===” 仅检查闭合范围,因此比使用 “==” 进行比较速度更快。

PHP 代码之外的性能瓶颈因素#

优化代码当然能够提高 PHP 的性能。但是,还有一些代码之外的因素也会成为 PHP 的性能瓶颈。这就是为什么程序员需要了解代码部署的整个服务器环境,这有助于他们在编写代码时有一定的心理准备,并能够在性能出现问题时,快速识别和定位性能瓶颈。以下是你遇到性能瓶颈时需要检查的点。

1. 网络带宽#

如果网络带宽不够,其传输的总数据量将会受到严重影响,使其成为最明显的性能瓶颈。

2. CPU#

如果只是传输一些纯静态的 HTML,则不需要消耗很多 CPU 资源,但是 PHP 毕竟创建的是动态的应用程序,根据应用的需要,你可能至少需要一台具备多核处理器的服务器来提升 PHP 代码的运行效率。

3. 共享内存#

缺少共享内存可能会影响进程间通信,从而影响程序性能。

4. 文件系统#

随着时间推移,你的文件系统可能会出现大量磁盘碎片。如果内存足够,利用内存作为文件缓存可以加快磁盘的访问速度。

5. 进程管理#

检查服务器的进程,确保里面没有非必要的进程。移除哪些不需要的网络协议、病毒扫描软件、邮件服务以及硬件驱动。将 PHP 代码运行在多线程模式,也能提高程序的响应时间。

6. 相关的其它服务#

如果你的应用程序还依赖于一些外部服务,那这些外部服务的性能瓶颈也有可能拖慢你的应用。虽然这种情况下你能做的事情不多,但你仍然可以通过你这一边的操作来减轻外部服务性能瓶颈对你的影响,例如切换到备用服务上等。

更多 PHP 性能优化建议#

1. 发挥 OPCache 的优势#

由于默认情况下,PHP 代码在执行时都会重新编译为可执行的中间代码 OPCode,因此可以及时看到修改的代码所带来的变化,而不必频繁的重启 PHP 服务。不幸的是,如果每次在你的网站上运行时,都重新编译相同的代码会严重影响服务器的性能,这就是为什么 opcode 缓存或 OPCache 非常有用。

OPCache 是一个将编译好的代码保存到内存中的扩展。因此,下一次代码执行时,PHP 将检查时间戳和文件大小,以确定源文件是否已更改。如果没有,则直接运行缓存的代码。

下图显示了运行无缓存的 PHP 应用程序,OPcache 和 eAccelerator(另一个 PHP 缓存工具)三者的执行时间和内存使用情况的差异。

图片来源: Prestashop#

2. 识别数据库响应延迟#

如上所述,性能问题并不总是由代码引起的。大多数瓶颈都出现在应用程序必须访问资源的时候。由于 PHP 应用程序的数据访问层可能占用最高 90% 的执行时间,因此你应该采取的第一步是查看代码中访问数据库的所有实例。

确保打开 SQL 的慢日志,以帮助你识别和处理慢 SQL,然后评估这些查询的执行效率。如果你发现查询过多,或者在单次执行过程中发现相同的查询被多次进行,你可以通过减少数据库访问时间进行调整,从而提高应用程序的性能。

3. 清理文件系统#

清理文件系统,并确保没有使用文件系统来存储 Session。最重要的是,请注意 file_exists (),filesize () 或 filetime () 等触发文件统计信息的代码。将任何这些功能置于循环中可能会导致性能问题。

4. 监控外部 API 接口#

大部分对外部系统有依赖关系的应用都会调用远程 API。虽然这些远程 API 接口你无法直接控制,但你仍可以采取一些措施来减轻源自远程 API 的性能问题。例如,你可以缓存 API 输出的数据,或者可以在后台调用这些 API。为 API 请求设置合理的超时时间,并且如果可能的话,随时做好 API 没有响应的情况下的显示输出。

5. 使用工具评估检测你的 PHP 代码#

使用 OPcache 和监控外部 API 接口应该足以使大多数应用程序运行顺利;但是,如果你发现系统负载不断增加,那么可能需要使用工具来对你的 PHP 代码进行检测评估。完整的 PHP 代码检测评估虽然可能很耗时,但它可以为你提供有关应用程序性能的深入信息。幸运的是,有几个开源程序可以用于分析你的 PHP 代码,如 Xdebug

监控 PHP 性能的重要性#

如果你没有做好准备,你的 Web 应用可能前一分钟还在正常运行,但是下一分钟,一波突然激增的流量就会导致你的应用程序崩溃。 当然,优化和重构总是需要时间、精力和资金,而且投入是否值得的也很难说。因此,做出明智决策的最佳方式是不断收集数据

PHP 性能监控软件可以帮助你立即测量所做的任何更改的影响。当然,知道要监测什么同样重要。速度和内存使用被认为是性能的最佳指标,因为它们影响到页面加载时间,这对 Web 应用程序至关重要。

虽然数据收集很重要,但是当你不需要监控系统时,你应该关闭监控系统,因为大量日志同样也会对性能造成影响。当然,这样的日志可以提供有关如何提高性能的有用信息,因此你应该在高峰期间定期监控。

未来的 PHP 性能#

PHP 仍在不断进化中,在目前正在开发的 PHP 8 版本中,最新的功能是即时编译或 JIT,它将可以为我们创建更快的 Web 应用。随着技术的不断进步,用户的期望也随之增加。因此,开发人员必须始终关注未来的变化。

在构建 Web 应用程序时,请记住,今年的工作可能在明年不起作用。你可能需要进行调整才能持续保持优秀的 PHP 性能。在开发过程中,应该持续重点关注如何构建适用于高并发场景的 Web 应用和网站,保证它们的高可用性。

参考文章#

Using a PHP CDN Setup
How to Install Nginx PHP

扫描下方二维码或者微信搜索 [phpjiagoushier],关注我的微信公众号 [PHP 架构],参与互动交流。

phpjiagoushier

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 7年前 加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 4
Artisan

greate

7年前 评论

额,数据库开启链接的成本是很高了毫秒级别那种,我自己测试过 Windows 10 ntfs 文件系统 PHP 7.1 mysql 8 , 1.5ms~3.4ms 有时候回事 10ms 以上~~可以使用 kcachegrind 查看 PDO::__contruct 函数查看建立链接的时间花销~~我就宁愿占用点内存,也不要开启一个走都 tcp 的数据链接~何况下一个请求来了还不是要占用~

7年前 评论
颜⑧

先点个赞,以后慢慢体会

7年前 评论