PHP性能优化

PHP语言级的性能优化

压力测试工具ab的使用

linux环境单独安装ab 压测工具

yum install httpd-tools

ab压测工具的使用

可参考此链接地址:http://www.ha97.com/4617.html
网站性能压力测试是性能调优过程中必不可少的一环。只有让服务器处在高压情况下才能真正体现出各种设置所暴露的问题。Apache中有个自带的,名为ab的程序,可以对Apache或其它类型的服务器进行网站访问压力测试。

ApacheBench命令原理:

ab命令会创建很多的并发访问线程,模拟多个访问者同时对某一URL地址进行访问。它的测试目标是基于URL的,因此,既可以用来测试Apache的负载压力,也可以测试nginx、lighthttp、tomcat、IIS等其它Web服务器的压力。

ab命令对发出负载的计算机要求很低,既不会占用很高CPU,也不会占用很多内存,但却会给目标服务器造成巨大的负载,其原理类似CC攻击。自己测试使用也须注意,否则一次上太多的负载,可能造成目标服务器因资源耗完,严重时甚至导致死机。
ApacheBench参数说明

ab -h #查看ab参数帮助文档

ab两个常用的参数说明:

#参数说明:
-n requests Number of requests to perform
//在测试会话中所执行的请求个数(本次测试总共要访问页面的次数)。默认时,仅执行一个请求。
-c concurrency Number of multiple requests to make
//一次产生的请求个数(并发数)。默认是一次一个。
[root@hotdata ~]# ab -n2000 -c500 http://www.lianjia.com/

-n后面的2000代表总共发出2000个请求;-c后面的500表示采用500个并发(模拟500个人同时访问),后面的网址表示测试的目标URL。

[root@hotdata ~]# ab -n2000 -c500 http://www.lianjia.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.lianjia.com (be patient)
Completed 200 requests
Completed 400 requests
Completed 600 requests
Completed 800 requests
Completed 1000 requests
Completed 1200 requests
Completed 1400 requests
Completed 1600 requests
Completed 1800 requests
Completed 2000 requests
Finished 2000 requests


Server Software:        openresty/1.9.7.1
Server Hostname:        www.lianjia.com
Server Port:            80
#测试的页面
Document Path:          /
#页面大小
Document Length:        0 bytes
#测试的并发数
Concurrency Level:      500
#整个测试持续的时间
Time taken for tests:   2.979 seconds
#完成的请求数量
Complete requests:      2000
#失败的请求数量
Failed requests:        0
Write errors:           0
Non-2xx responses:      2000
#整个过程中的网络传输量
Total transferred:      1045587 bytes
#整个过程中的HTML内容传输量
HTML transferred:       0 bytes
#最重要的指标之一,相当于LR中的每秒事务数,后面括号中的mean表示这是一个平均值
Requests per second:    671.46 [#/sec] (mean)
#最重要的指标之二,相当于LR中的平均事务响应时间,后面括号中的mean表示这是一个平均值
Time per request:       744.651 [ms] (mean)
#每个连接请求实际运行时间的平均值
Time per request:       1.489 [ms] (mean, across all 
concurrent requests)
#平均每秒网络上的流量,可以帮助排除是否存在网络流量过大导致响应时间延长的问题
Transfer rate:          342.81 [Kbytes/sec] received
#网络上消耗的时间的分解
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       35  232 186.2    239    1883
Processing:    60  357 254.6    268    2551
Waiting:       51  357 254.7    268    2551
Total:        100  589 331.1    519    2855
#整个场景中所有请求的响应情况。在场景中每个请求都有一个响应时间,
#其中50%的用户响应时间小于275毫秒,66%的用户响应时间小于298毫秒,
#最大的响应时间小于11843毫秒。对于并发请求,cpu实际上并不是同时处理的,
#而是按照每个请求获得的时间片逐个轮转处理的,所以基本上
#第一个Time per request时间约等于第二个Time per request时间乘以并发请求数。
Percentage of the requests served within a certain time (ms)
  50%    519
  66%    562
  75%    640
  80%    687
  90%    978
  95%   1309
  98%   1582
  99%   2092
 100%   2855 (longest request)

总结:在远程对web服务器进行压力测试,往往效果不理想(因为网络延时过大),建议使用内网的另一台或者多台服务器通过内网进行测试,这样得出的数据,准确度会高很多。如果只有单独的一台服务器,可以直接本地测试,比远程测试效果要准确。

多使用PHP内置函数

优化点:少写代码,多用PHP自身内置函数
性能问题:
自写代码冗余较多,可读性不佳,并且性能低

为什么性能低?
PHP代码需要编译解析为底层语言,这一过程每次请求都会处理一遍,开销大

好的方法:

多使用PHP内置变量、常量、函数

优化点:PHP内置函数的性能优劣

情况描述:PHP内置函数之间存在性能差异
好的建议:多去了解PHP内置函数的时间复杂度
例子:isset()优于array_key_exists();
参考资料:

提高性能:下一步该做什么?

减少PHP魔法函数的使用

优化点:尽可能的少用魔法函数
情况描述:PHP提供的魔法函数,性能不佳
为什么性能低?为了给PHP程序员省事,PHP语言为你做了很多
好的方法:尽可能的规避使用PHP魔法函数
测试__get()
使用魔法函数测试



/**
*测试魔法函数对性能的影响
* 使用魔法函数耗时:200ms
* 不使用魔法函数:80ms
*/
class test 
{
    private $var = '123';
    public function __get($argu)
    {
        return $this->var;
    }
}

//测试代码
$i = 0;
while ($i< 100000) {
    $i++;
    $test = new test();
    $test ->var;
    //这里是去访问对象中的私有属性var,我们知道私有属性只能在类内部访问,
    //所以这样是拿不到var的,可是我们在类的内部定义了魔法函数__get(),所以会去调用这个函数
}

Linux下测试可以用time,关注user的时间

[root@hotdata tmp]# time php test.php 

real    0m0.231s
user    0m0.198s
sys 0m0.032s
[root@hotdata tmp]# time php test.php 

real    0m0.237s
user    0m0.209s
sys 0m0.027s
[root@hotdata tmp]# time php test.php 

real    0m0.215s
user    0m0.194s
sys 0m0.020s
[root@hotdata tmp]# time php test.php 

real    0m0.228s
user    0m0.210s
sys 0m0.017s

不使用魔法函数测试

/**
*测试魔法函数对性能的影响
* 使用魔法函数耗时:15~20ms
*/
class test 
{
    public $var = '123';
    // public function __get($argu)
    // {
    //  return $this->var;
    // }
}

//测试代码

$i = 0;
while ($i< 100000) {
    $i++;
    $test = new test();
    $test ->var;
    //这里是去访问对象中公有属性var,没有使用魔法函数
}

测试执行时间,关注下方的user在80ms左右

[root@hotdata tmp]# time php test1.php 

real    0m0.084s
user    0m0.078s
sys 0m0.005s
[root@hotdata tmp]# time php test1.php 

real    0m0.089s
user    0m0.083s
sys 0m0.005s
[root@hotdata tmp]# time php test1.php 

real    0m0.093s
user    0m0.081s
sys 0m0.010s
[root@hotdata tmp]# time php test1.php 

real    0m0.091s
user    0m0.082s
sys 0m0.008s
[root@hotdata tmp]# 

禁用错误抑制符@

优化点:产生额外开销的错误抑制符@
情况描述:PHP提供的错误抑制符只是为了方便的“懒人”
@的实际逻辑:在代码开始前、结束后,增加Opcode,忽略报错
好的建议:尽量不要使用@错误抑制符
@会导致opcode变长从而影响效率
如果想查看zend执行的opcode,可以安装php 的 VLD扩展(下载源码 编译 在php.ini中开启(添加))。

下面就用PHP的扩展VLD来分析下。
安装VLD扩展

wget http://pecl.php.net/get/vld-0.13.0.tgz

tar zxvf vld-0.13.0.tgz 

cd vld-0.13.0

phpize

whereis php-config#找到php-config所在位置

./configure --with-php-config=/usr/bin/php-config --enable-vld

make && make install

vim /etc/php.ini 
extension = vld.so#在php.ini中加入此扩展

service php-fpm restart

vld扩展已经安装好了,开始分析下了
不使用错误抑制符,如下


#test.php
file_get_contents('xxx.txt'); 
[root@fengniu020 wk]# php -dvld.active=1 -dvld.execute=0 test.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /hotdata/kehu/wk/test.php
function name:  (null)
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   SEND_VAL                                                 'xxx.txt'
         1        DO_FCALL                                      1          'file_get_contents'
   3     2      > RETURN                                                   1

branch: #  0; line:     2-    3; sop:     0; eop:     2; out1:  -2
path #1: 0, 

使用错误抑制符测试


 @file_get_contents('xxx.txt');
[root@fengniu020 wk]# php -dvld.active=1 -dvld.execute=0 test.php
Finding entry points
Branch analysis from position: 0
Jump found. Position 1 = -2
filename:       /hotdata/kehu/wk/test.php
function name:  (null)
number of ops:  5
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   BEGIN_SILENCE                                    ~0      
         1        SEND_VAL                                                 'xxx.txt'
         2        DO_FCALL                                      1          'file_get_contents'
         3        END_SILENCE                                              ~0
   3     4      > RETURN                                                   1

branch: #  0; line:     2-    3; sop:     0; eop:     4; out1:  -2
path #1: 0, 

对比分析可知:@会导致opcode变长从而影响效率

合理的使用内存和正则表达式

优化点:尽量少用正则表达式
情况描述:正则表达式的回溯开销很大
好的建议:利用字符串处理函数,实现相同逻辑。如果能利用字符串处理函数完成需求,则尽量少用正则表达式。

合理使用内存

情况描述:PHP有内存回收机制,但也请小心使用内存
好的建议:虽然PHP有内存回收机制,但是使用unset()可及时释放不使用的内存,可提高程序性能。(注:unset()出现注销不掉的情况)

避免在循环内做运算

优化点:避免在循环内做运算
情况描述:循环内的计算式将会被重复计算
代码示例:


#错误写法
$str = 'test';
for($i=0;$i$str);$i++){
   echo $i;
}
#正确写法
$strlen = strlen($str);
for($i=0;$i<$strlen;$i++){
    echo $i;
}

减少计算密集型业务

优化点:减少计算密集型业务
情况描述:PHP不适合密集型运算的场景
为什么?PHP语言特性决定了PHP不适合做大数据量的运算
PHP试用场景:适合衔接Webserver与后端服务、UI呈现

使用带引号字符串做键值

优化点:务必使用带引号字符串做键值
情况描述:PHP会将没有引号的键值当作常量,产生查找常量的开销
好的建议:严格使用引号作为键值
示例:



define('key','imooc');
#define('key','imooc');

$array = array("key"=>'hello world',
        'imooc'=>'http://www.imooc.com'
);
echo $array['key'];#正确写法  hello world

echo $array[key];
#错误写法,输出http://www.imooc.com,
#这样会先找常量,没有找到则会抛出一个notice错误

PHP周边问题的优化

  • PHP运行环境,Linux or win
  • 文件存储 硬盘(机械硬盘或SSD)
  • 数据库
  • 缓存(redis or memcache,涉及硬件内存)
  • 网络(光纤或普通)
    PHP常见场景开销次序:
    开销:读写内存<<读写数据库<读写磁盘<读写网络数据(socket),后三个基于文件系统,数据库有其缓存系统,网络又有延迟。
    说明:
    1、<< 表示远小于
    2、尽量多使用读写数据库,读写内存,减少文件类操作
    3、数据库 相对 磁盘 有个缓存机制
    4、网络数据:基于socket句柄,还有延迟等之类的隐性因素

减少PHP发起网络请求

优化网络请求
如何优化网络请求?
1、设置超时时间

  • 设置连接超时 200ms
  • 设置读取超时 800ms
  • 设置写超时 500ms

2、将串行请求并行化

  • 使用curl_multi_*()
  • 使用swoole扩展

    PHP性能优化_第1张图片

压缩PHP输出的利与弊

压缩PHP接口输出
如何压缩?
使用Gzip即可

压缩输出的利与弊?
利:利于我们的数据输出,Client端可以更快的获取数据
弊:额外的CPU开销

使用GZip压缩,
特点:
(1)、对于少量数据几十K压缩效果不明显,另外压缩效果取决于文件重复字符数,重复越多压缩效果越好,反之,越差;
(2)、会额外增加server端和client的CPU开销,因为要压缩和解压缩

缓存重复计算的内容

什么情况下做输出内容的缓存?
多次请求,输出的内容不变的情况
流程:当用户访问时,若cache中有则返回,没有则从重新计算并加入cache,然后返回给用户

cached:     x.php-->cache-->output
no cache:   x.php-->count,compile-->cache---->output

重叠时间窗口思想

使用原则:后一个窗口(任务)不强依赖于前一个窗口(任务)的输出

说白了就是任务之间异步执行,异步执行的前提条件是后一个任务不存在强依赖前一个任务的情况,否则只能串行执行,比如注册的时候发送邮件这一过程不是强依赖注册过程的(运行有适当的延时,这种情况就可以使用异步处理)
串行: 请求的总和
并行: 由其中一个最长的请求决定
重叠时间窗口:可以理解为半并行(对任务的依赖有限制)

盘路处理方案

使用前提和重叠时间窗口一样,后一个任务不依赖于前一个任务,可考虑
PHP性能优化_第2张图片

借助xhprof工具分析PHP性能

参考此文档:xhprof扩展的安装和简单使用

总结

php性能瓶颈究极办法:
使用Opcode Cache:PHP扩展APC,yac等,可在官网查看很多可用的cache
扩展实现高频业务逻辑:通过PHP扩展代替原PHP代码中高频业务逻辑
Runtime优化:HHVM

需要开发模块的时候,可以先找一下,不用重复发明轮子:
http://pecl.php.net/packages.php

PHP7是个不错的选择,新版本逐渐在修复各种发现的问题,各大cms系统,都在紧锣密鼓的进行针对性的调试,它是必然要占据舞台中心的。

你可能感兴趣的:(PHP)