最近发现博客的内存老是隔三差五地被“吃掉”了,登录到后台后偶尔会出卡顿的情况,一开始怀疑是Swap不够导致的,于是给VPS主机增加了几个G的Swap,观察了一段时间后发现再大的Swap也被慢慢地“吃掉”了!
很显然是PHP某些服务一直在占用着VPS的内存没有释放,导致物理内存耗尽后调用了Swap,显然Swap没有物理内存运行的效率高,于是就出现了进程卡死的情况了。考虑到挖站否现在用的Wordpress用的主题与插件过多,出现这样的情况也是正常。
LNMP架构中PHP是运行在FastCGI模式下,按照官方的说法,php-cgi会在每个请求结束的时候会回收脚本使用的全部内存,但是并不会释放给操作系统,而是继续持有以应对下一次PHP请求。而php-fpm是FastCGI进程管理器,用于控制php的内存和进程等。
所以,解决的办法就是通过php-fpm优化总的进程数和单个进程占用的内存,从而解决php-fpm进程占用内存大和不释放内存的问题。更多的Linux服务器优化方法以及建站心得,还有:
PS:2018年12月14日更新,如果你的VPS主机的内存和性能不是很好的话,这时最好是启用缓存可以大大节省资源消耗:WordPress开启Nginx fastcgi_cache缓存加速方法-Nginx配置实例。
PS:2019年9月29日更新,由于Google主导开发的服务器优化神器ngx_pagespeed,集成了图片延迟加载、自适应webp、JS和CSS优化、图片优化等一整套优化工具:PageSpeed服务器优化神器-Nginx部署ngx_pagespeed模块和加速效果体验。
如果你发现VPS主机出现了卡顿的情况,首先查看一下内存的占用情况,常用的命令就是Top、Glances、Free等,不了解这些命令的朋友可以先看看挖站否做的专题:Linux系统监控命令整理汇总-掌握CPU,内存,磁盘IO等找出性能瓶颈。
使用Glances命令,再按下m,就可以查看到当前VPS主机进程内存占用情况了,按照占用内存由多到少排序(或者使用Top命令,按下M,效果是一样的)。如下图(点击放大):
这是一张重启后进程内存占用情况图,从前后对比中可以发现:随着开机时间的增长,php-fpm占用的内存越来越大,最终php-fpm耗尽了VPS所有物理内存。
查看当前php-fpm总进程数,命令:ps -ylC php-fpm --sort:rss
。其中RSS就是占用的内存情况。如下图:
查看当前php-fpm进程的内存占用情况及启动时间,命令如下:
ps -e -o 'pid,comm,args,pcpu,rsz,vsz,stime,user,uid'|grep www|sort -nrk5
从下图可以看出当前php-fpm所有进程平均每个进程占用了60-70MB的内存,启动时间,是当天的话就是3:12,否则会显示是X月X日。
查看当前php-fpm进程平均占用内存情况,一般来说一个php-fpm进程占用的内存为30-40MB,本次查询的结果是60MB,显然是多了。命令如下:
ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'
结果61M
php-fpm.conf就是php-fpm的配置文件,路径一般在:/usr/local/php/etc
,如下图:
php-fpm.conf几个重要的参数说明如下:
pm = dynamic #指定进程管理方式,有3种可供选择:static、dynamic和ondemand。
pm.max_children = 16 #static模式下创建的子进程数或dynamic模式下同一时刻允许最大的php-fpm子进程数量。
pm.start_servers = 10 #动态方式下的起始php-fpm进程数量。
pm.min_spare_servers = 8 #动态方式下服务器空闲时最小php-fpm进程数量。
pm.max_spare_servers = 16 #动态方式下服务器空闲时最大php-fpm进程数量。
pm.max_requests = 2000 #php-fpm子进程能处理的最大请求数。
pm.process_idle_timeout = 10s
request_terminate_timeout = 120
pm三种进程管理模式说明如下:
pm = static,始终保持一个固定数量的子进程,这个数由pm.max_children定义,这种方式很不灵活,也通常不是默认的。
pm = dynamic,启动时会产生固定数量的子进程(由pm.start_servers控制)可以理解成最小子进程数,而最大子进程数则由pm.max_children去控制,子进程数会在最大和最小数范围中变化。闲置的子进程数还可以由另2个配置控制,分别是pm.min_spare_servers和pm.max_spare_servers。如果闲置的子进程超出了pm.max_spare_servers,则会被杀掉。小于pm.min_spare_servers则会启动进程(注意,pm.max_spare_servers应小于pm.max_children)。
pm = ondemand,这种模式和pm = dynamic相反,把内存放在第一位,每个闲置进程在持续闲置了pm.process_idle_timeout秒后就会被杀掉,如果服务器长时间没有请求,就只会有一个php-fpm主进程。弊端是遇到高峰期或者如果pm.process_idle_timeout的值太短的话,容易出现504 Gateway Time-out错误,因此pm = dynamic和pm = ondemand谁更适合视实际情况而定。
static管理模式适合比较大内存的服务器,而dynamic则适合小内存的服务器,你可以设置一个pm.min_spare_servers和pm.max_spare_servers合理范围,这样进程数会不断变动。ondemand模式则更加适合微小内存,例如512MB或者256MB内存,以及对可用性要求不高的环境。
如果你的VPS主机的内存被占用耗尽,可以检查一下你的php-fpm进程数,按照php-fpm进程数=内存/2/30来计算,1GB内存适合的php-fpm进程数为10-20之间,具体还得根据你的PHP加载的附加组件有关系。
这里以1GB内存的VPS配置php-fpm为演示,实际操作来看设置数值还得根据服务器本身的性能、PHP等综合考虑。
pm = dynamic #dynamic和ondemand适合小内存。
pm.max_children = 15 #static模式下生效,dynamic不生效。
pm.start_servers = 8 #dynamic模式下开机的进程数量。
pm.min_spare_servers = 6 #dynamic模式下最小php-fpm进程数量。
pm.max_spare_servers = 15 #dynamic模式下最大php-fpm进程数量。
上面通过减少php-fpm进程总数来达到减少php-fpm内存占用的问题,实际使用过程中发现php-fpm进程还存长期占用内存而不释放的问题。解决的方法就是减少pm.max_requests数。
最大请求数max_requests,即当一个 PHP-CGI 进程处理的请求数累积到 max_requests 个后,自动重启该进程,这样达到了释放内存的目的了。以1GB内存的VPS主机设置为例(如果你设置的数值没有达到释放内存可以继续调低):
pm.max_requests = 500
当php-fpm进程达到了pm.max_requests设定的数值后,就会重启该进程,从而释放内存。下图是我测试后的效果,可以看出php-fpm进程被强制结束并释放了内存。
对于大内存以及对并发和可用性要求的话,建议使用static管理模式+最大的pm.max_children。如果是小内存的服务器,建议使用dynamic或者ondemand模式,同时降低pm.start_servers和pm.max_spare_servers进程数。
为什么我调整了参数没有达到应有的效果?根据wzfou.com的经验,php-fpm配置文件参数不能一概而论,必须要结合服务器自身的性能、WEB动态内容以及对可用性的要求来进行调整,内存长期占用最好是再检查一下是否有内存泄露。
2019年10月9日更新,如果你的php-fpm参数调整得过小,有可能出现502错误,解决办法:解决WordPress后台编辑保存菜单出现502错误。
文章出自:挖站否 https://wzfou.com/php-fpm/,版权所有。本站文章除注明出处外,皆为作者原创文章,可自由引用,但请注明来源。
文章更新于: 2020年9月26日 下午9:00
查看评论
QI大,我的单个php-fpm进程平均占用达到了247M....8G的VPS,这个是不是太大了点啊,是不是需要调低pm.max_requests啊?
8GB的话,如果负载低的话可以不用调,足够用了。
多谢~
这段时间也被这个内存一直不释放困扰,最近几天想着好好优化一下,包括网站主题和各种代码bug,在使用php缓存和数据库对象缓存的情况下,内存不再过山车,但是一直不断爬升,直到用完为止。今天百度到Qi的文章,试试添加个最大请求数看下,不知道效果会如何
可以试试,我现在的情况好多了。
[太开心]一个多小时过去了,效果很显著,感谢!
我的VPS用了宝塔面板,经常发现CPU跟内存占用都不高,但系统负载一直飙高,网页打开也比较慢。不知道是什么问题
那看看磁盘IO读写是不是占满了,另外看看iowait是不是达到了百分几十了,一般是百分一或者0
连上SSH,输入什么命令都没有反应了。
那应该是死机了。这种情况可以重启的时候持续观察,把一段时间的日志也下载下来,看看是不是有一些异常的IP,我以前也遇到这样的情况,基本上都是判定攻击了。
最新新购了一台经典CN2 GIA,查IP段和qi总的在同一个C段 :-D 想问问qi总所用的操作系统、php版本和WP优化插件,准备一模一样地配一台
我也购买了一台1GB的。现在用的操作系统是Ubuntu 18,PHP用的是7.1,感觉可能用7.2也不错,wp优化插件现在用的是redis插件,用来数据库加速。
之前配php7.2+mariadb10.3奇慢无比,也不知道是什么情况,现在我重新配成了debian9+php7.1+mariadb5.5(感觉debian会比ubuntu稳定),缓存用的是memcached配上W3 Total Cache(这个插件真的很强大,已经不用wp super cache了)。qi来测测速度怎么样?https://www.equestriacn.com/
秒开。
我的是php-cgi占用CPU过多. 内存占用不大. 请问是同样的配置么?
是的,也是WP吗?如果是WP的话,建议检查一下是不是有没有插件长期占用CPU,或者是进程。试着通过调整参数看看能不能释放。
出现牛顿?不是卡顿嘛?
哈哈,已经更新。
感觉对wordpress无爱了
为何?
WordPressCMS不是挺便利的吗?
自己写博客系统就是方便,每个函数用完都注销掉,内存畅爽无卡顿
这样很吊。
站长用的lnmp.org的还是oneinstack一键包?我记得是后者?
我自己目前改成ondemand了,但效果也不行,物理内存过小。不过,依稀记得在哪里见过一个公式用来计算动态模式下那几个什么pm.max之类的数量计算。
是的,用的oneinstack。有一个计算工具,但是我用了之后感觉还得根据自己的服务器配置还有PHP、Ningx等配置还自己调整,我的经验就是调整pm.max_requests,目前来看有效果了,你可以试着将pm.max_requests调小,然后看看最后的效果。
多谢。
这个内存和效率吧,说到底最简单的,还是加一个字。8G干不了的,双倍一下,16G就能应付。可惜我还停留在512MB上。
优化的效果不如翻一倍的内存。
这个用宝塔的话,可以直接做限制~
用面板还是挺方便的。