PHP性能问题,占整个项目性能问题一般占30%,不会超过50%。
要从全局去考虑全局的性能问题
源自慕课网视频教程整理:https://www.imooc.com/learn/205
什么情况下遇到PHP性能问题
- 1 :PHP语法使用不恰当
- 2 :使用了PHP语言他不擅长做的事情
- 3 :用PHP语言连接的服务不给力
- 4 :PHP自身的短板
- 5 :未知的问题
PHP性能问题解决方向 (从1~3的顺序,操作简单,见效快排序)
- A . PHP语言级的性能优化 :日常语法方法的优化 特点:简单高效很快见到效果
- B . PHP周边问题的性能优化 :网络环境,前面webserver,后面mysql
- C . PHP语言自身的分析和优化 :PHP底层C语言逻辑的优化
压力测试
作为一个PHP程序员必须要知道并且会使用常用的压力测试工具,对接口做压力测试
Apache Benchmark 简称ab
ab是由Apache提供的压力测试软件。安装apache服务器时会自带压测软件
使用 ./ab -n1000 -c100 http://www.baidu.com
+ -n请求数 -c并发数 url目标
- ab 返回结果的参数
- 1> Requests per second 每秒请求数 (优化目标 每秒的请求数尽可能多)
- 2> Time per request 响应一个请求耗时 (优化目标 响应一个请求尽可能少)
webbench
webbench -c 50 -t 30 http://127.0.0.1:6969/shenghuojiaofei/test
siege
- siege -c 并发数 -t 运行测试时间 URL
这里要注意的是-t后面的时间要带单位,s表示秒,如果不带,就是分钟。
Opcode
我们知道,拿到一段代码后,经过词法解析、语法解析等阶段后,源程序会被翻译成一个个指令(opcodes),然后ZEND虚拟机顺次执行这些指令完成操作。PHP本身是用C实现的,因此最终调用的也都是C的函数,实际上,我们可以把PHP看做是一个C开发的软件。
- PHP的执行的核心是翻译出来的一条一条指令,也即opcode,我们平时所说的缓存,一般也是缓存Opcode(APC,mecache,yac)。
Opcode是PHP程序执行的最基本单位。一个opcode由两个参数(op1,op2)、返回值和处理函数组成。PHP程序最终被翻译为一组opcode处理函数的顺序执行。
所以我们可以通过vld查看和分析Opcode,具体问题具体分析
PHP语言级性能优化
优化点:少些php代码,多用PHP自身能力,多用PHP自身的
性能问题:自写代码冗余较多,可读性不佳,并且性能低
php代码写的越长长执行效果就会越差,多用php自身的函数等
为什么性能低? :
PHP 代码需要编译为C语言,C语言又会编译成汇编语言(机器语言),这里每一个过程都会请求一遍,开销很大。尤其是访问量大的时候,每次都会编译一遍。所以要尽量减少代码
好的方法:多使用PHP内置的变量、常量、函数
原生PHP 和 自己写一段实现一个同样的功能,性能差异。有的可能会提高好几倍。
为什么自己实现的会慢呢? PHP代码如何再linux上执行?
*.php 通过zend引擎逐行扫描分析(Scanner),
保存成zend引擎自己能识别的语法(Exprs),
这些zend引擎能识别的语法,再解析(Parser)成Opcodes。
Opcodes是最终要拿去执行的机器代码 。执行,然后输出。
- 逐行扫描,转码,解析成Opcodes,然后输出, 扫描时间少了,zend转码时间会更快,解析Opcodes也会更快。
缓存服务都是缓存的Opcodes,就不用扫描和解析了,当然就更更快了。
###【PHP内置函数的性能优劣】
情况描述: PHP内置函数,之间依然存在快慢差异。使用快的函数。
- 数组,文件,日期的操作
- 多了解注释时间复杂度,大部分性能问题,都会涉及时间和控件的转换
###【尽量少用魔法函数】
为什么魔法函数性能低:
为了给程序员省事,php语言为你做了很多
+ linux time函数 可以直接测试程序的耗时情况 魔法函数举例:__get(); + 可以不用尽量不用,如果必须要用的时候再用。
###【不使用@ 错误抑制符】
@ 错误抑制符原理:在代码开始前、结束后,增加Opcode
前面提到了vld工具,我们这里可以在代码开始前、结束后,增加Opcode。查看一下Opcode执行码。
工具:
- vld PHP扩展 主要作用就是把opcode 演示出来。
1 | php -dvld.active=1 -dvld.execute=0 at.php |
回打印出opcode,结果用@ 会多2行代码,多操作逻辑,有多些开销。 可以用 try throw 这种。
###【合理使用内存】
情况描述:php有内存回收机制保底,但也要小心使用内存
建议:利用unset()及时释放不使用的内存(注:unset出现注销不掉的情况,自己查资料)
###【尽量少的使用正则表达式】
情况描述:正则表达式性能低,因为正则表达式回溯开销较大
好的建议:利用字符串处理函数,实现相同的逻辑
###【避免循环内做运算】
- 情况描述:循环内的计算式会被重复计算
例如
1 | for($i=0;$i<strlen($str);$i++) |
每一次for循环都会进行计算strlen
###【减少计算密集型业务】
- 情况描述:PHP不适合密集型运算场景,在处理大数据量的时候性能比较差
为什么:
+ 比如不适合大批量日志分析,或者大批量数据处理。
+ php语言特性决定了PHP不适合做大数据运算
+ php所有处理都需要转换成C语言,与C相比,C更好。
+ php还有环境问题,还有语言特性。额外开销比C大很多。变量寄存等。。
- PHP适合什么?
以下是PHP官方手册提供的PHP支持
PHP 脚本主要用于以下三个领域:
1 |
|
所以总结来说:PHP适合衔接webserver与后端服务、UI呈现。(就是接口,简单数据处理,和套页面)
###【务必使用带引号字符串做键值】
PHP周边问题的性能优化
- PHP周边都有什么
- 1.linux环境
- 2.硬盘,文件存储,php读写
- 3.数据库,表设计
- 4.缓存 缓存是基于内存的
- 5.网络,带宽
抓大头去优化,就是先从大的方向去做优化
###【减少文件类操作】
- 常见PHP场景的开销次序:
- 读写磁盘、
- 读写数据库、
- 读写内存、
- 读写网络数据
读写内存 <<< 读写数据库 < 读写磁盘 < 读写网络数据: socket操作文件句柄
###【减少php发起网络请求】
优化网络请求
- 网络请求的坑:1. 对方接口的不确定因素 2. 网络稳定性
如何优化网络请求
######1设置超时时间 : 建议值
a)连接超时 200ms 这是上限,最多也不能超过这个时间
b)读超时 800ms 这个看具体情况
c)写超时 500ms 建议不要超过500ms
######2、将串行请求并行化
a)使用curl_multi_*() 返回时间是看用时最长的那个请求
b)使用swoole扩展 通过C来进行并行化。推荐使用
###【压缩PHP接口输出】
如果用php做接口如何更高效的输出
如何压缩 : 使用Gzip
压缩输出的利弊:
- 利:利于数据输出,client能更快获取数据
- 弊:额外的CPU开销。如果请求大,肯能会有问题
gzip如果数据量小于几十K的时候效果并不理想。如果大于100k,压缩就有效果。
###【缓存重复计算内容】
什么情况下做输出内容缓存 : 固定重复请求的数据做缓存。
###【重叠时间窗口思想】 串行变并行
如果后一个请求不强依赖于前一个返回值。就可以变成并行,降低总体时间消耗
- 条件:后一个任务不强依赖于前一个任务
###【旁路方案】 也是重叠时间窗口思想
- 条件:后一个任务不强依赖于前一个任务
其他高级优化
- 读写分离
- 分布式
- 集群
- CDN
- 服务器架构
后期会有相应的文章具体学习与实战
PHP性能问题的具体分析
工具: XHPorf(源自fackbook的PHP性能分析工具)
实践:通过分析wordpress程序,做优化。
- php –ri 扩展名 查看php是否支持某个扩展
- linux 下搜索文件中具体代码在哪个文件
- grep ‘xxx’ ./ -r
其他工具
- ab,webbench,siege - 压力测试
- vld - opcode代码分析
PHP性能瓶颈解决方法
Opcode Cache :PHP扩展APC就是做Opcode缓存用的
- Apc 可能不常用了,可以再pecl点caching。查看php缓存的扩展
- pecl 是官方经过验证的PHP扩展
扩展实现:通过PHP扩展代替原PHP代码中高频的逻辑,开销比较大的
- Runtime优化:HHVM (phpng也许更优于HHVM)
实战总结整理
- 1、 用单引号代替双引号来包含字符串,这样做会更快一些。因为 PHP 会在双引号包围的 字符串中搜寻变量,单引号则不会,
- 注意:只有 echo 能这么做,它是一种可以把多个字符 串当作参数的“函数”(译注:PHP 手册中说 echo 是语言结构,不是真正的函数,故把函数 加上了双引号)。
- 2、如果能将类的方法定义成 static,就尽量定义成 static,它的速度会提升将近 4 倍。
- 3、$row[‘id’] 的速度是$row[id]的 7 倍。
- 4、echo 比 print 快,并且使用 echo 的多重参数(译注:指用逗号而不是句点)代替字符串 连接,比如 echo $str1,$str2。
- 5、在执行 for 循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用 foreach 代替。
- 6、注销那些不用的变量尤其是大数组,以便释放内存。
- 7、尽量避免使用get,set,__autoload。
- 8、require_once()代价昂贵。
- 9、include 文件时尽量使用绝对路径,因为它避免了 PHP 去 include_path 里查找文件的速 度,解析操作系统路径所需的时间会更少。
- 10、如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用 $_SERVER[‘REQUEST_TIME’] 要好于 time()
- 11、函数代替正则表达式完成相同功能。
- 12、str_replace 函数比 preg_replace 函数快,但 strtr 函数的效率是 str_replace 函数的四倍。
- 13、如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么 可以考虑额外写一段替换代码, 使得每次传递参数是一个字符, 而不是只写一行代码接受数 组作为查询和替换的参数。
- 14、使用选择分支语句(译注:即 switch case)好于使用多个 if,else if 语句。
- 15、用@屏蔽错误消息的做法非常低效,极其低效。
- 16、打开 apache 的 mod_deflate 模块,可以提高网页的浏览速度。
- 17、数据库连接当使用完毕时应关掉,不要用长连接。
- 18、错误消息代价昂贵。
- 19、在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。
- 20、递增一个全局变量要比递增一个局部变量慢 2 倍。
- 21、递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢 3 倍。
- 22、递增一个未预定义的局部变量要比递增一个预定义的局部变量慢 9 至 10 倍。
- 23、仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局 部变量)。PHP 大概会检查看是否存在全局变量。
- 24、方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了 10 个方法,但性能上没有变化。
- 25、派生类中的方法运行起来要快于在基类中定义的同样的方法。
- 26、调用带有一个参数的空函数,其花费的时间相当于执行 7 至 8 次的局部变量递增操作。 类似的方法调用所花费的时间接近于 15 次的局部变量递增操作。
- 27、Apache 解析一个 PHP 脚本的时间要比解析一个静态 HTML 页面慢 2 至 10 倍。尽量 多用静态 HTML 页面,少用脚本。
- 28、除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套 PHP 缓存机制通常 可以提升 25%至 100%的性能,以免除编译开销。
- 29、尽量做缓存,可使用 memcached。memcached 是一款高性能的内存对象缓存系统, 可用来加速动态 Web 应用程序,减轻数据库负载。对运算码 (OP code)的缓存很有用,使 得脚本不必为每个请求做重新编译。
30、 当操作字符串并需要检验其长度是否满足某种要求时, 你想当然地会使用 strlen()函数。
- 此函数执行起来相当快,因为它不做任何计算,只返回在 zval 结构(C 的内置数据结构,用 于存储 PHP 变量)中存储的已知字符串长度。
- 但是,由于 strlen()是函数,多多少少会有些 慢,因为函数调用会经过诸多步骤,如字母小写化(译注:指函数名小写化,PHP 不区分函 数名大小写)、哈希查找,会跟随被调用的函数一起执行。
在某些情况下,你可以使用 isset() 技巧加速执行你的代码。
(举例如下)
1
2
3if (strlen($foo) < 5) {
echo “Foo is too short”$$
}
(与下面的技巧做比较)
1
2
3
if (!isset($foo{5})) {
echo “Foo is too short”$$
}
调用 isset()恰巧比 strlen()快,因为与后者不同的是,isset()作为一种语言结构,意味着它 的执行不需要函数查找和字母小写化。
> 也就是说, 实际上在检验字符串长度的顶层代码中你 没有花太多开销。
- 31、当执行变量$i 的递增或递减时,$i++会比++$i 慢一些。这种差异是 PHP 特有的,并不 适用于其他语言, 所以请不要修改你的 C 或 Java 代码并指望它们能立即变快, 没用的。
- ++$i 更快是因为它只需要 3 条指令(opcodes),$i++则需要 4 条指令。
- 后置递增实际上会产生一 个临时变量,这个临时变量随后被递增。
- 而前置递增直接在原值上递增。这是最优化处理的 一种,正如 Zend 的 PHP 优化器所作的那样。
- 牢记这个优化处理不失为一个好主意,因为 并不是所有的指令优化器都会做同样的优化处理, 并且存在大量没有装配指令优化器的互联 网服务提供商(ISPs)和服务器。
- 32、并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很 多内存。
- 33、并非要用类实现所有的数据结构,数组也很有用。
- 34、不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?
- 35、当你需要时,你总能把代码分解成方法。
- 36、尽量采用大量的 PHP 内置函数。
- 37、如果在代码中存在大量耗时的函数,你可以考虑用 C 扩展的方式实现它们。
- 38、 评估检验(profile)你的代码。 检验器会告诉你, 代码的哪些部分消耗了多少时间。 Xdebug 调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈。
- 39、mod_zip 可作为 Apache 模块,用来即时压缩你的数据,并可让数据传输量降低 80%。
- 40、在可以用 file_get_contents 替代 file、fopen、feof、fgets 等系列方法的情况下,尽量 用 file_get_contents,因为他的效率高得多!但是要注意 file_get_contents 在打开一个 URL 文件时候的 PHP 版本问题;
- 41、尽量的少进行文件操作,虽然 PHP 的文件操作效率也不低的;
- 42、优化 Select SQL 语句,在可能的情况下尽量少的进行 Insert、Update 操作(在 update 上,我被恶批过);
- 43、尽可能的使用 PHP 内部函数(但是我却为了找个 PHP 里面不存在的函数,浪费了本可 以写出一个自定义函数的时间,经验问题啊!);
- 44、 循环内部不要声明变量, 尤其是大变量: 对象(这好像不只是 PHP 里面要注意的问题吧?);
- 45、多维数组尽量不要循环嵌套赋值;
- 46、在可以用 PHP 内部字符串操作函数的情况下,不要用正则表达式;
- 47、foreach 效率更高,尽量用 foreach 代替 while 和 for 循环;
- 48、用单引号替代双引号引用字符串;
- 49、“用 i+=1 代替 i=i+1。符合 c/c++的习惯,效率还高”
- 50、对 global 变量,应该用完就 unset()掉;
推荐:http://baijiahao.baidu.com/s?id=1600263247562743733&wfr=spider&for=pc