PHP -fpm 服务器内存占用陡坡式上涨,请问如何彻底解决?

2020-11-19 11:04:56 +08:00
 reyleon
我手上维护着两台 Web 服务器,后端运行的是 PHP 接口服务,服务器配置为 8 核 16G,PHP 版本为 5.6.40 ,运行在 PHP-FPM 模式下,php-fpm 进程的内存使用情况随着请求数的增加而不断上涨,系统可用内存呈陡坡式下降,一不留神就会有内存告警但我们又处理不够及时而导致业务奔溃的可能。

这个问题让我黯然伤神,不知该如何是好?

我查了一下,几乎全网都在告诉你调整 php-fpm 运行模式,如 pm 改为 dynamic 或 static 以及调整 php-fpm 子进程数量,或者就是让你调低 pm.max_requests 的值,让它达到请求数时自动销毁进程以释放内存。

我们就是这么做的,虽然有点用,但这明显不是治本的办法,这只是一种妥协。

让我疑惑的是,对于内存的释放,我也查到不同的说法:

说法一(来自 Swoole 官方微信公众号)

FPM 自带黑魔法,传统的跑在 FPM 下的 PHP 代码是没有“内存泄漏”一说的。因为 PHP 内核有一个关键函数叫做 php_request_shutdown,此函数会在请求结束后,把请求期间申请的所有内存都释放掉,这从根本上杜绝了内存泄漏。


说法二(来自 PHP 开发组核心成员博客)

PHP 之所以会在请求结束后正确的释放掉所有的资源,内存,这是因为当我们在脚本中使用新的内存的时候,PHP 会向 OS 申请一大块内存(ZEND_MM_SEG_SIZE 大小),然后分给你你需要的合适的一块小内存。

当你不使用这块小内存的时候,PHP 也不会返还给 OS,而是保留下来给后续的处理使用。所以,如果你使用完了资源不及时释放,那么后续的逻辑如果请求内存,PHP 发现之前申请的一大块内存已经分光了,它就只好再次向 OS 发起 malloc 调用,得到一块新的大内存。 并且它还需要对这个大内存做一些标记处理。

而如果你使用完资源,及时释放的话,那么下次脚本申请内存的时候,你之前归还的内存块就可以被重复利用,那么也许你的整个脚本只需要和 OS 申请一次内存。

如果你买了一本 PHP 的书,它告诉你: “不用在 PHP 主动释放资源,因为 PHP 会帮你释放”的话,我建议你,烧了它。


说法三(来自 PHP 官方手册)

引用计数系统是 Zend 引擎的一部分,可以自动检测到一个资源不再被引用了(和 Java 一样)。这种情况下此资源使用的所有外部资源都会被垃圾回收系统释放。因此,很少需要手工释放内存。


以上说法,不知道哪个是正确的。但事实是存在的,php-fpm 进程的内存使用就是会上涨。

有没有大神指点一下,如何深入分析内存上涨的原因?假设要深入 PHP 代码,有没有行之有效的分析工具?

(注:我不是 php 程序员)。
5214 次点击
所在节点    程序员
63 条回复
joyqi
2020-11-19 17:40:05 +08:00
php 的性能没有很多人想象得那么烂,这个帖子里很多人的服务器资源称得上奢侈了。楼主提供的信息有限,你的服务器并发量现在是多少,平均响应时间是多少,是否存在慢查询,服务器的内存占用监控图是否可以提供下。
JasperYanky
2020-11-19 17:45:13 +08:00
加机器!

业务跑的好:这么赚钱的业务,加点机器怎么了?
业务跑的不好:业务这么差还让人投入精力改代码改配置?加机器就完事了!
ladypxy
2020-11-19 17:53:19 +08:00
不需要的模块不要加载
换 php 7.3
php 效率其实很高,一般都是你设置问题
reyleon
2020-11-19 18:30:28 +08:00
@Evilk 你可能看错了。“请求结束,进程关闭” 这是 3 楼说的,不是我说的,你搞错对象了。哈哈哈
reyleon
2020-11-19 18:39:16 +08:00
@joyqi 并发量其实很低,目前日 PV 90 万不到,我之前自己做过压测,我们这个接口服务器性能其实很差,估计并发撑不过 40,但性能问题目前并不是我急需解决的。

主要是 php-fpm 进程吃内存,会慢慢往上涨,这才是我想快点解决的。
如果不设置 pm.max_requests, 它可以吃完机器所有的内存。

另:V2EX 貌似无法上图?
zhenhuaYang
2020-11-19 19:18:44 +08:00
@liuxu 666666 啊
MeteorCat
2020-11-19 19:23:24 +08:00
ulimit 多少
seth19960929
2020-11-19 19:34:06 +08:00
如果 php-fpm 有内存泄漏,不太可能有这么低级的错误
我从优先级给你排查
你的代码有调用系统命令吗?或者守护进城服务,比如 kafka

是否有定时任务执行常驻内存的 PHP 脚本任务

你在内存占用高的时候 top 一下看有几个 work 进程
liuxu
2020-11-19 19:47:47 +08:00
@zhenhuaYang 又被你发现了
CODEWEA
2020-11-19 20:18:38 +08:00
又来黑 php,你的 pv 才 90 万,就算是设置成 static=30 都没问题,要想找到解决办法,还得分析具体业务
ben1024
2020-11-19 20:49:20 +08:00
@shanghai1998
不太赞同换语言就能彻底解决并发问题,其他语言优势是有,但是开发者本身水平才是关键
everyx
2020-11-19 21:47:48 +08:00
@ben1024 之前用 https://github.com/DarkGhostHunter/Preloader 这个给项目上了 preloading,你可以试试
anerevol
2020-11-19 22:40:44 +08:00
可能是下面说的这个问题么
你这个基本属于能必现的问题 理论上用二分法很快能定位问题的
https://bugs.php.net/bug.php?id=76436

[2019-08-14 21:50 UTC] phpbug at ethaniel dot com
I have this problem in PHP 5.6.40 on Centos 7.6.

This simple code triggers it. I just read around 1 million rows from the table and my memory usage is just growing higher and higher.

$cnt = 0;
$result = mysqli_query($conn,"SELECT `text`,`sms_date`,`to` FROM `sms_data`.`sms_201933`;");
while ($row = mysqli_fetch_object($result)) {

if ($cnt%1000) {
echo memory_get_usage()." *** \n\n";
}

$cnt++;

}
huangsen365
2020-11-19 23:57:43 +08:00
用 remi php 7.x
用 docker 构建镜像
上多节点上负载均衡
数据库使用读写分离
IDAEngine
2020-11-20 01:35:54 +08:00
没遇到过,内存上涨不释放很大原因是代码写的有问题
reyleon
2020-11-20 09:24:44 +08:00
@anerevol 感谢!不过我看了下,应该不是这个问题,我搜索了一下,代码中并没有涉及 mysqli 的代码,事实上都没有连接 MySQL 服务。有连接的只有 redis
oneend
2020-11-20 09:48:18 +08:00
@reyleon
试试加上 opcache
rqrq
2020-11-20 10:24:23 +08:00
就是代码问题,自己一点点的去掉代码查吧。
之前用 swoole 弄了个 http server,按照文档 “捕获 Server 运行期致命错误” 加了 register_shutdown_function 放到 onRequest 里面,结果就是内存泄漏。
onion83
2020-11-20 10:26:09 +08:00
安利一下 Swoole tracker https://business.swoole.com/tracker/index
litujin1123
2020-11-20 13:41:09 +08:00
@reyleon 那不是也应该排查一下 redis 部分?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/727028

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX