如何找出PHP进程占用CPU高的幕后元凶
业务反馈在进入页面时遇到白屏,无法正常打开浏览器页面进行学习。经检查发现,PHP进程的CPU占用率异常高。考虑到本周末的使用人数人数应该不会很大,理论上系统资源的使用不应如此之高。
因此,技术团队的第一反应是某个PHP进程可能陷入了死循环,导致资源占用过高。
排查
Step1 通过 htop 命令
过 htop
命令查看CPU和内存占用情况。按 Shift + M
键按内存使用量排序。
由上图可见,可以看出占用CPU最高的TOP 2是 http://0.0.0.0:8782 RestyService
这个服务。
这两个进程CPU占用率接近81.2%
。CPU时间片主要是被17472
和17487
这两个进程给吃掉了, 所以目标锁定在17472
和17487
这两个线程。
Step2 查看进程状态
通过命令php start.php status
查看进程状态
可以看出进程[17487]和[17475]
状态进程状态
是[busy]
,[busy]
状态代表是繁忙
如果进程进入短暂的繁忙是正常情况,如果进程一直是繁忙状态,则有可能发生了业务阻塞或者业务死循环的情况。PS:idle
代表空闲。
Step3 调试[busy]繁忙进程
有时候我们通过php start.php status
命令能看到有busy状态的进程,说明对应进程正在处理业务。
正常情况下业务处理完毕对应进程会恢复为idle
状态,这一般情况下不会有什么问题。
但是如果一直是busy
状态没有恢复过idle状态,则说明进程内的业务有阻塞或者无限循环,可以通过以下方法定位。
status
里找到busy
进程的pid
,这里就拿17487
这个进程来排查。
Step4 strace 跟踪进程
挑选一个进程pid(这里选择17487
),获取当前进程中各进程的调用栈。
运行 sudo strace -ttp 17487
显示如下
可以看到进程在不断的循环poll([{fd=9, events=....
的系统调用,这是在等待fd
为9
的描述符可读事件,也就是在等这个描述符返回数据。
注意: 如果没有显示任何系统调用,保留当前终端,重新再打开一个终端,运行
kill -SIGALRM 17487
(给进程发送一个闹钟信号),然后看strace
的终端是否有响应,是否阻塞在某个系统调用上。如果仍然没有显示任何系统调用说明程序很可能处于业务死循环中。
Step5 lsof 查看进程描述符
用
lsof -p $pid | grep $fd
查看进程在等待哪个外部资源的返回
运行 lsof -p 17487 | grep 9
显示如下
进程描述符9
对应的是9u
的记录(最后一行),能看fd=9
的描述符是一个tcp连接。
远程地址是172.18.207.82:mysql
,说明进程应该是在访问一个数据库资源,循环poll([{fd=9, events=....
是一直在等待数据库服务端返回数据,这解释了为什么进程处于busy状态。
确保一下是否是否数据库连接主机
ping rm-xxxxxxxxxxx.mysql.rds.aliyuncs.com
PING rm-xxxxxxxxxxx.mysql.rds.aliyuncs.com (172.18.207.82) 56(84) bytes of data.
64 bytes from 172.18.207.82 (172.18.207.82): icmp_seq=1 ttl=102 time=1.93 ms
64 bytes from 172.18.207.82 (172.18.207.82): icmp_seq=2 ttl=102 time=1.91 ms
64 bytes from 172.18.207.82 (172.18.207.82): icmp_seq=3 ttl=102 time=1.90 ms
通过上述htop
中能看到busy
进程占用cpu
很高(81%)
。基本断定代码里有无限死循环,在通过strace
查看系统调用,可以看出是一条SQL在执行一个死循环
`sendto(9,"} \0\0\0\26SELECT id FROM sg_organ"...., 129 MSG DONTWAIT, NULL,0 = 129`
Step6 排查死循环SQL
排查导致死循环的SQL语句时。while
循环是一个常见的潜在问题源。
不过,死循环不仅限于 while
循环,还可能由其他类型的循环结构或不当的触发器使用引起。但在此,我们主要聚焦于 while
循环导致的死循环问题。
最终定位到死循环代码
通过代码最终定位到的SQL
语句
SELECT `id` FROM `sg_organ_xxx` WHERE `id` = 2025
验证
将while
循环代码修改提交部署重新部署后,重新登录服务器查看项目CPU和内存占用情况
此时PHP进程的CPU占用已经恢复正常了!
来源:开源技术小栈