2020-08-10-PHP性能分析

Posted by 初始台地 on August 10, 2020

影响PHP性能的主要因素

  1. 网络带宽
  2. CPU
  3. 需要连接其他子服务
  4. 数据库
  5. 复杂的业务逻辑、糟糕的代码
  6. 架构是否合理
  7. 。。。

怎么去优化他

  1. 增大带宽
  2. 增加服务器配置
  3. 优化响应慢的子服务,或去掉不必要的子服务等
  4. 数据库分库分表加索引、使用缓存(redis/mongodb)
  5. 优化业务逻辑、优化代码提高代码质量
  6. 开启opcache
  7. 。。。

关于OPCache

PHP-FPM + Nginx 的工作机制

请求从Web浏览器到Nginx,再到PHP处理完成,一共要经历如下五个步骤:

  1. 启动PHP-FPM
    • PHP-FPM 支持两种通信模式:TCP socket和Unix socket;
    • PHP-FPM 会启动两种类型的进程:Master 进程 和 Worker 进程,前者负责监控端口、分配任务、管理Worker进程;后者就是PHP的cgi程序,负责解释编译执行PHP脚本。
    • 启动Nginx。首先会载入 ngx_http_fastcgi_module 模块,初始化FastCGI执行环境,实现FastCGI协议请求代理
  2. Request => Nginx
    • Nginx 接收请求,并基于location配置,选择一个合适handler
  3. Nginx => PHP-FPM
    • Nginx 把请求翻译成fastcgi请求
    • 通过TCP socket/Unix Socket 发送给PHP-FPM 的master进程
  4. PHP-FPM Master => Worker
    • PHP-FPM master 进程接收到请求
    • 分配Worker进程执行PHP脚本,如果没有空闲的Worker,返回502错误
    • Worker(php-cgi)进程执行PHP脚本,如果超时,返回504错误
    • 处理结束,返回结果
  5. PHP-FPM Worker => Master => Nginx
    • PHP-FPM Worker 进程返回处理结果,并关闭连接,等待下一个请求
    • PHP-FPM Master 进程通过Socket 返回处理结果
    • Nginx Handler顺序将每一个响应buffer发送给第一个filter → 第二个 → 以此类推 → 最终响应发送给客户端
PHP脚本解释执行的机制
运行一段PHP代码主要需要以下四个步骤
1
2
3
4
1. php初始化执行环节,启动Zend引擎,加载注册的扩展模块
2.初始化后读取脚本文件,Zend引擎对脚本文件进行词法分析(lex),语法分析(bison),生成语法树
3. Zend 引擎编译语法树,生成opcode,
4. Zend 引擎执行opcode,返回执行结果
  • 在PHP cli模式下,每次执行PHP脚本,四个步骤都会依次执行一遍;
  • 在PHP-FPM模式下,第一步在PHP-FPM启动时执行一次,后续的请求中不再执行;第2-4布每个请求都要执行一遍;
  • 其实第2、3步生成的语法树和opcode,同一个PHP脚本每次运行的结果都是一样的,
  • 此时就需要进行优化,以提升性能,主要有以下几个方案
    1. OPCache:前身是Zend Optimizer+ ,是 Zend Server 的一个开源组件;官方出品,强力推荐
    2. APC:Alternative PHP Cache 是一个开放自由的 PHP opcode 缓存组件,用于缓存、优化 PHP 中间代码;已经不更新了不推荐
    3. APCu:是APC的一个分支,共享内存,缓存用户数据,不能缓存opcode,可以配合Opcache 使用 4.eAccelerate:同样是不更新了,不推荐
    4. xCache:不再推荐使用了
OPcache一些配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
opcache.revalidate_freq
    这个选项用于设置缓存的过期时间(单位是秒),当这个时间达到后,opcache会检查你的代码是否改变,如果改变了PHP会重新编译它,生成新的opcode,并且更新缓存。值为“0”表示每次请求都会检查你的PHP代码是否更新(这意味着会增加很多次stat系统调用

opcache.validate_timestamps
    当这个选项被启用(设置为1),PHP会在opcache.revalidate_freq设置的时间到达后检测文件的时间戳(timestamp)
    如果这个选项被禁用(设置为0),opcache.revalidate_freq会被忽略,PHP文件永远不会被检查。这意味着如果你修改了你的代码,然后你把它更新到服务器上,再在浏览器上请求更新的代码对应的功能,你会看不到更新的效果,你必须得重新加载你的PHP(使用kill -SIGUSR2强制重新加载)。
    
 opcache.max_accelerated_files
    这个选项用于控制内存中最多可以缓存多少个PHP文件。这个选项必须得设置得足够大,大于你的项目中的所有PHP文件的总和
    
opcache.memory_consumption
    设置可使用的内存大小,单位MB

opcache.interned_strings_buffer
    这是一个很有用的选项,但是似乎完全没有文档说明。PHP使用了一种叫做字符串驻留(string interning)的技术来改善性能。例如,如果你在代码中使用了1000次字符串“foobar”,在PHP内部只会在第一使用这个字符串的时候分配一个不可变的内存区域来存储这个字符串,其他的999次使用都会直接指向这个内存区域。这个选项则会把这个特性提升一个层次——默认情况下这个不可变的内存区域只会存在于单个php-fpm的进程中,如果设置了这个选项,那么它将会在所有的php-fpm进程中共享。在比较大的应用中,这可以非常有效地节约内存,提高应用的性能。
    
opcache.fast_shutdown
    另外一个很有用但也没有文档说明的选项。从字面上理解就是“允许更快速关闭”。它的作用是在单个请求结束时提供一种更快速的机制来调用代码中的析构器,从而加快PHP的响应速度和PHP进程资源的回收速度,这样应用程序可以更快速地响应下一个请求。把它设置为1就可以使用这个机制了。

怎么找出具体问题在哪

  1. 人工体验哪个接口慢然后看代码
  2. 看Nginx日志请求、响应时间 a. request_time 从接受用户请求的第一个字节到发送完响应数据的时间,即包括接收请求数据时间、程序响应时间、输出响应数据时间 b. upstream_response_time 从 nginx 向后端(php-cgi)建立连接开始到接受完数据然后关闭连接为止的时间 ``` 整个过程具体是以下5步 1.用户请求 2.建立Nginx连接 3.发送响应 4.接收响应 5.关闭Nginx连接

    1
    2
    3
    4
    5
    
     其中
         request_time由1-5组成
         upstream_response_time2-5组成
     而 1.用户请求 的时间根据用户的网络环境、传输数据本身大小有关,所以一般情况下看upstream_response_time的时间即可
    ```
    
  3. 慢sql
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
     # 多少秒以上的查询算是慢查询
     long_query_time=2 
        
     #5.0、5.1等版本配置如下选项 
     log-slow-queries=/var/log/mysql/slow_query.log
        
     #5.5及以上版本配置如下选项 
     slow-query-log=On
     slow_query_log_file=/var/log/mysql/slow_query.log
        
     查询是否开启成功
     mysql> show VARIABLES like "%query%";
    

    image

  4. 通过各种工具 a. Xdebug 来源:https://zhuanlan.zhihu.com/p/53985558
    • Xdebug 是一个开源的PHP调试器。以PHP扩展的形式安装到PHP中,典型功能有:
      • 单步调试,single step debug
      • 调试版的 var_dump()
      • 增加了错误,异常的 stack traces
      • 可记录每个函数调用和变量的分配
      • 增加了一个分析器 profiler
      • 为 PHPUnit 提供了一个代码覆盖(code coverage)功能

    b. xhprof

    • xhprof是一个PHP轻量级性能分析工具
    • 函数级别的请求次数和各种指标
      • 阻塞时间
      • CPU时间
      • 内存使用情况
      • 函数的调用时长和次数
    • 有一个简单的HTML用户界面,并能生成关系调用图 c. OneAPM

    Xdebug与xhprof

    1. xhprof相比较Xdebug更轻量级,cpu消耗更低,使之放在生产环境上成为一种可能
    2. 可在代码中自由控制是否开启性能分析
      1
      2
      3
      4
      5
      6
      7
      
       文档上有一种用法以万分之一的几率启用xhprof,悄没声的打几枪
       if (mt_rand(1, 10000) == 1) {
           // 开启xhprof
           xhprof_enable(XHPROF_FLAGS_MEMORY);
           // 程序结束时保存xhprof信息
           register_shutdown_function(create_funcion('', "$xhprof_data = xhprof_disable(); save $xhprof_data;"));
       }
      
    3. 可以很直观的看出函数级的请求时间等

    http://xhprof.fermin.link/?run=5f17a9e0cd228 image

    http://xhprof.fermin.link/callgraph.php?run=5f17a9e0cd228 image

    • 是通过Graphviz画出的图形

xhprof的安装和使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
1、安装
    (1)下载和解压
        wget http://pecl.php.net/get/xhprof-0.9.4.tgz
        tar zxvf xhprof-0.9.4.tgz
    (2)编译和运行
        cd xhprof-0.9.4/extension/
        phpize //此语句编译PHP扩展的工具,主要是根据系统信息生成对应的configure文件,一般存放在/usr/local/php/bin/目录下
        ./configure --with-php-config=/usr/local/php/bin/php-config
        make && make install
        mkdir /tmp/xhprof
    (3)编辑php.ini
        [xhprof]
        extension = xhprof.so
        xhprof.output_dir=/tmp/xhprof 
        xhprof.output_dir是分析生成日志的保存路径
    
    (4)安装插件 主要是UI的配置
        yum -y install libjpeg freetype freetype-devel libjpeg-devel liberation-sans-fonts.noarch
    自动安装
    
        yum -y install graphviz
    
    (5)安装HTML用户界面项目

        git clone https://github.com/preinheimer/xhprof.git
    
        这是一个常规的php项目,新增站点指向xhprof_html下即可
        
    (6)重命名文件 xhprof_lib 目录下的 config.sample.php 为 config.php。编辑 config.php 文件,进行配置
    
        配置数据库和URL选项:
    
        $_xhprof['dbhost'] = '127.0.0.1';
        $_xhprof['dbuser'] = 'root';
        $_xhprof['dbpass'] = '123456';
        $_xhprof['dbname'] = 'xhprof';
        $_xhprof['url'] = 'http://xhprof.dev.com';
        对于开发环境,设置IP控制为false,并将其他行注释,如下:
        
         $controlIPs = false;
        /*
        $controlIPs = [];
        $controlIPs[] = "127.0.0.1"; // localhost, you'll want to add your own ip here
        $controlIPs[] = "::1"; // localhost IP v6
        */
    
    (7)导入数据库
        在xhprof_lib/utils/xhprof_runs.php大概109行左右有建表sql
    
    (8)被分析网站配置
        打开需要分析的网站的Nginx配置文件,加入如下一行:
        
        location ~ .*\.(php|php5)?$ {
            #...
            fastcgi_param PHP_VALUE "auto_prepend_file=/项目路径/external/header.php";
         }
        如果被分析网站用的是Apache,则这样配置:
        
        <VirtualHost *:80>
          ...
          php_admin_value auto_prepend_file "/项目路径/external/header.php"
          ...
        </VirtualHost>
        这样header.php文件会在目标脚本执行之前自动解析执行。
    
    (9)开始分析
        在url上加入参数_profile=1即可
        这样网站加载完成的同时,会把分析数据保存在MySQL数据库中
    
    
        默认XHProf UI不会对PHP应用收集分析数据,在请求任意URL时,需添加GET参数_profile=1来启用。
    
        external/header.php脚本会检查_profile参数,并将参数值写到cookie中setcookie('_profile', $_GET['_profile']);,这样就不用每次请求都带GET参数_profile=1,并且cookie是针对域名的,这样也就同域名下的其他URL请求启用了性能分析,然后对目标URL去掉参数_profile后发起重定向。对于不带GET参数_profile的URL请求,header.php会继续检查是否存在名为_profile的cookie,如果存在且值为布尔真,则设置条件变量启用性能分析,否则不启用。
        
        若想要对已启用性能分析的域名禁用性能分析,则可以通过对URL请求添加GET参数_profile=0来禁用,因为header.php在检查cookie时发现_profile值为布尔假(0),所以不会启用性能分析。
        
    (10) 不更改配置,直接在业务逻辑中插入代码(不推荐)
        // 找到你要分析的代码,在代码开始处添加
        xhprof_enable(XHPROF_FLAGS_MEMORY);
        
        
        
        // 具体业务逻辑代码
        
        //在代码结束位置添加
        $xhprof_data = xhprof_disable();
        include_once ("~/xhprof/xhprof_lib/utils/xhprof_lib.php");
        include_once ("~/xhprof-0.9.4/xhprof_lib/utils/xhprof_runs.php");
        $xhprof_runs = new \XHProfRuns_Default();
        $xhprof_runs->save_run($xhprof_data, "xhprof_foo");
        
    (11)访问(5)配置的xhprof UI站点即可查看分析结果
术语说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    在查看 Xhprof 或 XHGUI 性能数据时,会遇到以下几个术语,其含义对应如下:
    
    Calls / Call Count:函数被调用的次数
    Incl. Wall Time / Wall Time:执行该函数(包括子函数)耗费的时间
    Incl. MemUse / Memory Usage:该函数(包括子函数)占用的内存
    Incl. PeakMemUse / Peak Memory Usage:函数(包括子函数)占用内存的峰值
    Incl. CPU / CPU:执行该函数(包括子函数)花费的CPU时间
    Excl. Wall Time / Exclusive Wall Time:函数本身(不包括子函数)耗费的时间
    Excl. MemUse / Exclusive Memory Usage:函数本身(不包括子函数)占用的内存
    Excl. PeakMemUse / Exclusive Peak Memory Usage:函数本身(不包括子函数)耗费内存的峰值
    Exclusive CPU:函数本身(不包括子函数)花费的CPU时间
    Inclusive简写Incl,表示测量到的数据是函数本身及所有调用的子函数总共耗费占用的资源。
    
    Exclusive简写Excl,则表示不包含调用的子函数耗费占用的资源。

参考资料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
https://www.awaimai.com/1652.html

https://blog.csdn.net/qinglianluan/article/details/48828717

https://mp.weixin.qq.com/s?src=11&timestamp=1595514580&ver=2478&signature=4oQBsWJHx8msWk0Z8LIJYRMbxtG2WnokzgN8mPLLOGjSdzT3c7H5tunz9pVk689leDclG*cO8I3mPPIrQimZqSxg6pw5Kora4Xa6xInYD3VP2za*FKDrhGehS6YwQPPN&new=1

https://blog.csdn.net/yzongyu/article/details/8457209?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

https://www.sunzhongwei.com/draw-the-flow-chart-tools-graphviz-introduction-and-basic-tutorial


http://note.youdao.com/s/B1UZbfQp

http://note.youdao.com/s/HlGHMWvA