前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >内存泄漏篇〡如何找出PHP进程占用CPU高的幕后元凶

内存泄漏篇〡如何找出PHP进程占用CPU高的幕后元凶

作者头像
Tinywan
发布2025-01-07 08:21:30
发布2025-01-07 08:21:30
13700
代码可运行
举报
文章被收录于专栏:开源技术小栈开源技术小栈
运行总次数:0
代码可运行

开源技术小栈导读:本文详细记录了一次PHP进程CPU占用率过高的问题和排查思路及其排查过程。经过初步检查,发现PHP进程的CPU占用率异常太高。技术团队的第一直觉是某个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时间片主要是被1747217487这两个进程给吃掉了, 所以目标锁定在1747217487这两个线程。

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=....的系统调用,这是在等待fd9的描述符可读事件,也就是在等这个描述符返回数据。

开源技术小栈注意: 如果没有显示任何系统调用,保留当前终端,重新再打开一个终端,运行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状态。

开源技术小栈确保一下是否是否数据库连接主机

代码语言:javascript
代码运行次数:0
复制
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在执行一个死循环

代码语言:javascript
代码运行次数:0
复制
`sendto(9,"} \0\0\0\26SELECT id FROM sg_organ"...., 129 MSG DONTWAIT, NULL,0 = 129`

Step6 排查死循环SQL

排查导致死循环的SQL语句时。while 循环是一个常见的潜在问题源。

不过,死循环不仅限于 while 循环,还可能由其他类型的循环结构或不当的触发器使用引起。但在此,我们主要聚焦于 while 循环导致的死循环问题。

最终定位到死循环代码

通过代码最终定位到的SQL语句

代码语言:javascript
代码运行次数:0
复制
SELECT `id` FROM `sg_organ_xxx` WHERE  `id` = 2025

验证

while 循环代码修改提交部署重新部署后,重新登录服务器查看项目CPU和内存占用情况

此时PHP进程的CPU占用已经恢复正常了

总结

在软件开发过程中,除了确保基本功能完整性这一核心要求外,对性能的考量同样至关重要。开发者在进行日常需求开发时,必须时刻保持对性能影响的警觉,这涵盖了代码执行效率、资源占用、以及系统响应速度等多个方面。

性能问题往往具有隐蔽性,它们可能不会立即显现,而是在系统负载增加或数据量扩大时才逐渐浮出水面。因此,开发者需要主动采取措施来预防和检测潜在的性能瓶颈。这包括在开发阶段就进行性能测试,利用专业的测试工具来模拟真实场景下的系统行为,从而准确评估系统的性能指标。

同时,开发者还应具备良好的代码优化习惯。在编写代码时,应尽量避免使用低效的算法和数据结构,而是选择那些经过优化且适合当前需求的解决方案。此外,对于数据库操作等关键路径上的代码,更应进行细致的性能调优,确保它们能够以最优的方式运行。

当发现性能问题时,开发者应迅速定位并解决问题。这可能需要利用调试工具进行逐步分析,或者查阅相关的文档和资料来寻找解决方案。在解决问题的过程中,开发者应保持冷静和耐心,避免因为急于求成而引入新的错误或问题。

综上所述,开发者在进行需求开发时,必须时刻关注性能问题,确保系统能够在满足功能需求的同时,也具备良好的性能和稳定性。只有这样,我们才能为用户提供更加优质和可靠的产品和服务。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 排查
    • Step1 通过 htop 命令
    • Step2 查看进程状态
    • Step3 调试[busy]繁忙进程
    • Step4 strace 跟踪进程
    • Step5 lsof 查看进程描述符
    • Step6 排查死循环SQL
  • 验证
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档