平时站点CPU使用率都在10%以内,最近发现达到了50%左右
top查看服务器资源使用情况:
%Cpu0 : 41.9 us, 1.3 sy, 0.0 ni, 56.1 id, 0.3 wa, 0.0 hi, 0.3 si, 0.0 st KiB Mem : 1016164 total, 68120 free, 583512 used, 364532 buff/cache KiB Swap: 0 total, 0 free, 0 used. 251176 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 5705 www 20 0 281272 40360 4516 S 15.0 4.0 0:09.09 php-fpm 5704 www 20 0 281292 40604 4584 S 8.0 4.0 0:08.89 php-fpm 5706 www 20 0 281124 41124 5296 S 8.0 4.0 0:08.80 php-fpm 5707 www 20 0 281328 40596 4580 S 7.7 4.0 0:08.77 php-fpm
分析top的结果,我们可以看出是FPM占用了过多的CPU资源。那么问题就清晰了:FPM为什么占用了这么多CPU资源,如何分析,如何解决?
带着这些问题,思考&解决思路整理如下:
0、查看nginx error和php-fpm slowlog, 排除应用本身的明显异常问题。
1、查看PHP是否开启OPcache缓存。
OPcache通过将PHP脚本预编译的字节码存储到共享内存中来提升PHP的性能,存储预编译字节码的好处就是省去了每次加载和解析PHP脚本的开销。
在phpinfo中或者php.ini中查看是否开启了OPcache,如果没有开启之。
官方建议php.ini中关于opcache的设置如下,更多参数参考这里:
[opcache] zend_extension=/usr/local/php/lib/php/extensions/no-debug-zts-20170718/opcache.so ; OPcache打开/关闭开关。当设置为Off或者0时,会关闭Opcache, 代码没有被优化和缓存。 opcache.enable=1 ; OPcache共享内存存储大小。用于存储预编译的opcode(以MB为单位)。 opcache.memory_consumption=64 ; 用来存储临时字符串的内存大小,以兆字节为单位. opcache.interned_strings_buffer=8 ; 这个选项用于控制内存中最多可以缓存多少个PHP文件。 opcache.max_accelerated_files=4000 ; 从缓存不被访问后,等待多久后(单位为秒)调度重启. opcache.force_restart_timeout=180 ; 这个选项用于设置缓存的过期时间(单位是秒),当这个时间达到后,opcache会检查你的代码是否改变,如果改变了PHP会重新编译它,生成新的opcode,并且更新缓存。 opcache.revalidate_freq=60 ;如果启用,则会使用快速停止续发事件。 所谓快速停止续发事件是指依赖 Zend 引擎的内存管理模块 一次释放全部请求变量的内存,而不是依次释放每一个已分配的内存块。 opcache.fast_shutdown=1 ; CLI环境下,PHP启用OPcache。这主要是为了测试和调试。从 PHP 7.1.2 开始,默认启用。 opcache.enable_cli=1
生产环境实际值参考(6C CPU,16G内存,300G硬盘):
opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=1 opcache.fast_shutdown=1 opcache.enable_cli=1 opcache.save_comments=0 opcache.enable_file_override=1 我这里的原因就是没有开启Opcache,开启后的top结果如下,可以看到效果很明显: %Cpu(s): 7.7 us, 0.3 sy, 0.0 ni, 91.3 id, 0.7 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 1016164 total, 160036 free, 498056 used, 358072 buff/cache KiB Swap: 0 total, 0 free, 0 used. 317092 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 5815 www 20 0 328876 34644 19648 S 2.3 3.4 0:00.59 php-fpm 5816 www 20 0 328856 29796 14644 S 1.7 2.9 0:00.46 php-fpm 5817 www 20 0 328856 27304 13188 S 1.3 2.7 0:00.45 php-fpm 5818 www 20 0 328856 36976 22816 S 1.3 3.6 0:00.93 php-fpm
2、strace工具调试一下看瓶颈在哪里。(strace一篇比较友好的入门介绍参考这里)
比如使用某些框架,需要动态的打开文件,框架是否支持优化的方案,到框架的官网上去看下,一般都有,另外框架一般默认加载了大而全的东西,检查下如果不是需要的是否可不加载。
[root@izj6cfhaw27k49x8usszs3z ~]# strace -c -p 5818 strace: Process 5818 attached ^Cstrace: Process 5818 detached % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 33.14 0.000056 2 24 sendto 28.40 0.000048 1 50 poll 17.75 0.000030 1 50 recvfrom 9.47 0.000016 8 2 setsockopt 6.51 0.000011 0 78 read 1.78 0.000003 0 11 10 lstat 0.59 0.000001 0 5 open 0.59 0.000001 0 11 close 0.59 0.000001 0 31 access 0.59 0.000001 0 8 getdents 0.59 0.000001 0 4 openat 0.00 0.000000 0 1 write 0.00 0.000000 0 12 stat 0.00 0.000000 0 5 fstat 0.00 0.000000 0 13 lseek 0.00 0.000000 0 8 rt_sigaction 0.00 0.000000 0 1 rt_sigprocmask 0.00 0.000000 0 4 setitimer 0.00 0.000000 0 1 socket 0.00 0.000000 0 1 1 connect 0.00 0.000000 0 1 accept 0.00 0.000000 0 1 shutdown 0.00 0.000000 0 1 getsockopt 0.00 0.000000 0 5 fcntl 0.00 0.000000 0 2 times ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000169 330 11 total
从上图可以看到耗时最长的是sendto操作,可以单独跟踪下sendto操作
[root@izj6cfhaw27k49x8usszs3z ~]# strace -T -e sendto -p 5818 strace: Process 5818 attached sendto(5, "S\0\0\1\215\242\n\0\0\0\0\300!\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 87, MSG_DONTWAIT, NULL, 0) = 87 <0.000017> sendto(5, "\22\0\0\0\3SET NAMES utf8mb4", 22, MSG_DONTWAIT, NULL, 0) = 22 <0.000011> sendto(5, "5\0\0\0\3SET NAMES 'utf8mb4' COLLATE"..., 57, MSG_DONTWAIT, NULL, 0) = 57 <0.000010> sendto(5, "\32\0\0\0\3SELECT @@SESSION.sql_mode", 30, MSG_DONTWAIT, NULL, 0) = 30 <0.000011> sendto(5, ".\0\0\0\3SET SESSION sql_mode='NO_EN"..., 50, MSG_DONTWAIT, NULL, 0) = 50 <0.000009> sendto(5, "\6\0\0\0\2niliu", 10, MSG_DONTWAIT, NULL, 0) = 10 <0.000011> sendto(5, "H\0\0\0\3SELECT option_name, option_"..., 76, MSG_DONTWAIT, NULL, 0) = 76 <0.000018> sendto(5, "Y\0\0\0\3SELECT option_value FROM wp"..., 93, MSG_DONTWAIT, NULL, 0) = 93 <0.000025> sendto(5, "N\0\0\0\3SELECT option_value FROM wp"..., 82, MSG_DONTWAIT, NULL, 0) = 82 <0.000022>
可以看到没有很明显的异常
3、尝试优化业务,DB之前尽量增加Cache。
参考:
《strace 跟踪进程中的系统调用》
《通过Strace定位故障原因》
《Opcode是啥以及如何使用好Opcache》
《PHP加速器之opcache配置详解》