开源技术小栈导读:本文详细记录了一次PHP进程CPU占用率过高的问题和排查思路及其排查过程。经过初步检查,发现PHP进程的CPU占用率异常太高。技术团队的第一直觉是某个PHP进程很可能陷入了死循环,导致其不断占用大量CPU资源,从而引发这一问题。
本周末业务反馈在进入页面时遇到白屏,无法正常打开浏览器页面进行学习。经检查发现,PHP进程的CPU占用率异常高。考虑到本周末的使用人数人数应该不会很大,理论上系统资源的使用不应如此之高。
因此,技术团队的第一反应是某个PHP进程可能陷入了死循环,导致资源占用过高。
过 htop
命令查看CPU和内存占用情况。按 Shift + M
键按内存使用量排序。
由上图可见,可以看出占用CPU最高的TOP 2是 http://0.0.0.0:8782 RestyService
这个服务。
这两个进程CPU占用率接近81.2%
。CPU时间片主要是被17472
和17487
这两个进程给吃掉了, 所以目标锁定在17472
和17487
这两个线程。
通过命令php start.php status
查看进程状态
可以看出进程[17487]和[17475]
状态进程状态
是[busy]
,[busy]
状态代表是繁忙
如果进程进入短暂的繁忙是正常情况,如果进程一直是繁忙状态,则有可能发生了业务阻塞或者业务死循环的情况。PS:idle
代表空闲。
有时候我们通过php start.php status
命令能看到有busy状态的进程,说明对应进程正在处理业务。
正常情况下业务处理完毕对应进程会恢复为idle
状态,这一般情况下不会有什么问题。
但是如果一直是busy
状态没有恢复过idle状态,则说明进程内的业务有阻塞或者无限循环,可以通过以下方法定位。
status
里找到busy
进程的pid
,这里就拿17487
这个进程来排查。
挑选一个进程pid(这里选择17487
),获取当前进程中各进程的调用栈。
运行 sudo strace -ttp 17487
显示如下
可以看到进程在不断的循环poll([{fd=9, events=....
的系统调用,这是在等待fd
为9
的描述符可读事件,也就是在等这个描述符返回数据。
开源技术小栈注意: 如果没有显示任何系统调用,保留当前终端,重新再打开一个终端,运行
kill -SIGALRM 17487
(给进程发送一个闹钟信号),然后看strace
的终端是否有响应,是否阻塞在某个系统调用上。如果仍然没有显示任何系统调用说明程序很可能处于业务死循环中。
开源技术小栈用 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`
排查导致死循环的SQL语句时。while
循环是一个常见的潜在问题源。
不过,死循环不仅限于 while
循环,还可能由其他类型的循环结构或不当的触发器使用引起。但在此,我们主要聚焦于 while
循环导致的死循环问题。
最终定位到死循环代码
通过代码最终定位到的SQL
语句
SELECT `id` FROM `sg_organ_xxx` WHERE `id` = 2025
将while
循环代码修改提交部署重新部署后,重新登录服务器查看项目CPU和内存占用情况
此时PHP进程的CPU占用已经恢复正常了
在软件开发过程中,除了确保基本功能完整性这一核心要求外,对性能的考量同样至关重要。开发者在进行日常需求开发时,必须时刻保持对性能影响的警觉,这涵盖了代码执行效率、资源占用、以及系统响应速度等多个方面。
性能问题往往具有隐蔽性,它们可能不会立即显现,而是在系统负载增加或数据量扩大时才逐渐浮出水面。因此,开发者需要主动采取措施来预防和检测潜在的性能瓶颈。这包括在开发阶段就进行性能测试,利用专业的测试工具来模拟真实场景下的系统行为,从而准确评估系统的性能指标。
同时,开发者还应具备良好的代码优化习惯。在编写代码时,应尽量避免使用低效的算法和数据结构,而是选择那些经过优化且适合当前需求的解决方案。此外,对于数据库操作等关键路径上的代码,更应进行细致的性能调优,确保它们能够以最优的方式运行。
当发现性能问题时,开发者应迅速定位并解决问题。这可能需要利用调试工具进行逐步分析,或者查阅相关的文档和资料来寻找解决方案。在解决问题的过程中,开发者应保持冷静和耐心,避免因为急于求成而引入新的错误或问题。
综上所述,开发者在进行需求开发时,必须时刻关注性能问题,确保系统能够在满足功能需求的同时,也具备良好的性能和稳定性。只有这样,我们才能为用户提供更加优质和可靠的产品和服务。