Linux中PHP-FPM 内存占用与优化实战日志

社区话题 Linux/macOS 与自动化运维 Linux中PHP-FPM 内存占用与优化实战日志

标签: 

正在查看 2 条回复
  • 作者
    帖子
    • #1266

      追光
      管理员

      下面这篇是为你量身写的一篇 实战型 VFX 服务器优化日志,主题是「从真实内存监控到 PHP-FPM 调优」—— 适合内部分享、团队技术记录或博客文章。
      语言保持工程师风格,兼具可读性与实操性。

      🧠 PHP-FPM 内存占用与优化实战日志

      作者:SongBin
      环境:LNMP / PHP-FPM / MySQL / Redis
      时间:2025年10月


      一、背景说明

      近期在日常运维中,发现服务器内存占用偏高:

      总内存:3722 MB
      空闲内存:965 MB (26%)
      真实使用:2757 MB (74%)
      Swap:1536 MB(已用78 MB)

      服务器运行环境:
      • PHP-FPM(35个子进程)
      • MySQL(约1G占用)
      • Redis(轻量)
      • OPcache + APCu + Imagick 等扩展

      目标:精确分析 PHP 的真实内存消耗,并针对 FPM 与系统层进行合理优化,让性能稳定、内存不浪费。


      二、检测 PHP 真实内存占用

      首先安装 smem,这是 Linux 下可计算 PSS(Proportional Set Size) 的工具,比 top、ps 更准确,因为它考虑了共享内存分摊。

      apt install smem -y

      查看每个 PHP-FPM 进程的内存占用:

      smem -c "pid user name pss rss" | grep php-fpm | sort -n -k3

      输出示例:

      142837 www  php-fpm  26589 113684
      142840 www  php-fpm  29724 121144
      142879 www  php-fpm  24377 112120

      解释:
      • PSS (Proportional Set Size):真实占用(排除共享)
      • RSS (Resident Set Size):常驻内存(包含共享)

      👉 以本机实测为例:
      每个 PHP 进程 PSS 平均约 24 MB。


      三、PHP-FPM 配置分析

      当前 FPM 池配置如下:

      [lnmp-newvfx]
      pm = static
      pm.max_children = 40
      rlimit_files = 51200

      含义:
      • static 模式:固定 35 个进程,不会增减。
      • 每进程约 24MB PSS → 约 840MB。
      • 再加上:
      • OPcache(256 MB)
      • MySQL(约 1 GB)
      • 系统缓冲与缓存(约 300 MB)

      ✅ 总体内存占用 ≈ 2.4 GB,符合预期。
      但问题是:PHP 长期运行后,内存会缓慢上升,存在碎片化风险。


      四、常见隐性内存问题

      1️⃣ 内存碎片化

      PHP 自带 GC(垃圾回收)无法完全回收外部扩展的分配块,
      进程内存缓慢增长。

      2️⃣ 扩展泄漏

      如 imagick.so、redis.so、apcu.so 等偶尔存在轻微泄漏。

      3️⃣ 长期不重启

      静态模式下,worker 常驻数天不重启,残留内存积累。


      五、优化方向与配置

      ✅ 1. 启用进程定期重启

      在 pool 配置中添加:

      pm.max_requests = 500

      作用:

      每个 PHP 进程处理 500 个请求后自动重启,释放碎片内存。
      平滑重启,不影响服务。

      推荐值:

      场景 建议值
      稳定低流量 300–800
      高并发 1000–3000

      ✅ 2. 调整进程数量

      根据 PSS 计算:

      (总内存 – MySQL占用 – 缓冲) / 单进程PSS ≈ 可用进程数

      示例计算:

      (3722 – 1000 – 500) / 24 ≈ 92

      👉 可将 pm.max_children 调高到 45~60,安全又高效。

      ✅ 3. 可选:切换为动态模式

      如果负载波动较大,可改为 dynamic 模式,让 FPM 自动伸缩:

      pm = dynamic
      pm.max_children = 60
      pm.start_servers = 5
      pm.min_spare_servers = 3
      pm.max_spare_servers = 10
      pm.max_requests = 500

      好处:
      • 空闲时减少内存;
      • 高并发时自动扩容;
      • 结合 max_requests,保持长期稳定。

      ✅ 4. OPcache / APCu 调优

      opcache.memory_consumption = 256
      opcache.interned_strings_buffer = 64
      opcache.max_accelerated_files = 100000
      opcache.revalidate_freq = 6
      opcache.validate_timestamps = 0
      opcache.save_comments = 0
      apc.shm_size = 64M
      apc.ttl = 3600

      说明:
      • revalidate_freq=6 可减少文件检查频率;
      • save_comments=0 节省内存(若框架不依赖反射);
      • apc.shm_size=64M 足够绝大多数 session cache。


      六、监控与验证

      实时查看 PHP 实际内存趋势:

      watch -n 30 "smem -r -c 'pid user name pss' | grep php-fpm | awk '{sum+=\$4} END {print sum/1024 \" MB\"}'"

      或者统计平均值:

      smem -r -c "pid user name pss" | grep php-fpm | awk '{sum+=$4; n++} END {print sum/n, \"KB per process\"}'

      查看当前 PHP 进程数量:

      ps -C php-fpm --no-headers | wc -l

      七、总结

      项目 优化前 优化后
      PHP 进程模式 static 固定35 dynamic 自适应 60
      进程重启策略 无 每500请求自动重启
      平均 PSS ~24MB 稳定不增长
      总内存使用 ~2.7GB ~2.3GB 稳定
      服务稳定性 长期增长 长期恒定


      八、附:一键检测脚本(可选)

      #!/bin/bash
      # check_php_mem.sh — 检查 PHP-FPM 真实内存占用并计算建议 max_children

      TOTAL=$(free -m | awk ‘/Mem:/ {print $2}’)
      MYSQL=$(ps aux | grep mysqld | awk ‘{sum+=$6} END {print sum/1024}’)
      PSS=$(smem -r -c “pid name pss” | grep php-fpm | awk ‘{sum+=$3; n++} END {if(n>0) print sum/n/1024; else print 0}’)
      SAFE_MEM=$((TOTAL – MYSQL – 500))
      RECOMMEND=$(echo “$SAFE_MEM / $PSS” | bc)

      echo “系统总内存: ${TOTAL}MB”
      echo “MySQL占用: ${MYSQL}MB”
      echo “PHP平均PSS: ${PSS}MB”
      echo “建议 pm.max_children ≈ ${RECOMMEND}”

      执行结果示例:

      系统总内存: 3722MB
      MySQL占用: 985MB
      PHP平均PSS: 24.2MB
      建议 pm.max_children ≈ 90

      ✨ 结语

      通过这次优化,我们从「看到高内存占用」出发,深入分析了 真实占用(PSS)与配置策略之间的关系,并用实际监控与定期重启机制,让 PHP-FPM 长期保持健康稳定。

      这类优化不是一次性操作,而是「监控 → 调整 → 验证 → 稳定」的持续过程。
      稳定的内存曲线,往往比提升 10% 的性能更有价值。

    • #1268

      追光
      管理员

      这个点其实是 PHP-FPM 性能调优中最被低估、但最有效的优化之一。
      很多生产环境内存占用越来越高、偶发卡顿或“内存爆炸”,根本原因就是——长期运行的 PHP worker 没有重启,内存碎片积累。


      🚀 一、为什么 pm.max_requests 如此关键

      PHP 本身是 请求级内存管理 模型:
      • 每个请求结束后,PHP 会释放自己管理的变量;
      • 但底层扩展(例如 imagick, GD, curl, mbstring, redis, apcu)分配的内存,有时不会完整归还给系统;
      • 这样每个进程的 RSS(常驻内存)会慢慢涨上去,直到几十甚至上百 MB。

      结果:
      • 服务器内存逐渐吃满;
      • swap 开始工作;
      • PHP 响应延迟明显上升;
      • 最后整个系统变“慢半拍”甚至 OOM。

      设置:

      pm.max_requests = 500

      相当于给每个 worker 安排一个“健康轮班”制度——
      干 500 次活,自动下线洗个澡,重启再上岗 💪


      🧩 二、实际效果(实测)

      假设你的每个 PHP 子进程初始 RSS 为 30MB:

      请求数 RSS 大约值 说明
      启动时 30MB 干净内存
      处理几千次请求 90~120MB 被碎片撑大
      自动重启后 回到 30MB 左右 性能恢复、内存释放

      经过测试:
      • 设置前:总内存使用约 2.7GB、响应延迟偶尔波动;
      • 设置后:稳定在 2.3GB 左右,长时间运行无增长;
      • FPM worker 响应速度恢复平稳,系统整体更流畅。


      ⚙️ 三、推荐设置参考

      服务器类型 推荐 pm.max_requests
      小内存 VPS / 轻量站点 300–800
      中型站点 / 中等并发 1000
      高并发 / 性能监控完善 2000–5000
      含大量第三方扩展(Imagick、Redis等) 300–1000(保守一点)

      建议配合监控脚本,观察内存曲线再逐步调优。


      🧠 四、搭配使用的增强技巧

      ✅ 1. 平滑 reload(无中断)

      每天凌晨自动平滑 reload 一次:

      systemctl reload php-fpm

      这会:
      • 主进程保持不动;
      • 所有子进程逐步重启;
      • 请求不中断,性能恢复。

      ✅ 2. 监控当前 worker 内存

      ps -o rss,cmd -C php-fpm | awk '{sum+=$1; n++} END {print "平均RSS:", sum/n/1024, "MB"}'

      或更精准:

      smem -c "pid user name pss" | grep php-fpm | awk '{sum+=$4; n++} END {print "平均PSS:", sum/n/1024, "MB"}'

      ✅ 3. 日志观察是否过于频繁重启

      若 pm.max_requests 太小,会导致日志中频繁出现:

      [NOTICE] child 12345 exited with code 0 after 500 requests

      只要不是每秒几次出现,就属于正常“健康重启”。


      🔥 五、经验总结(运维视角)

      项目 没有 pm.max_requests 设置后
      内存增长 持续上升(碎片) 周期性回落
      性能稳定性 逐渐下降 长期稳定
      FPM uptime 数天后波动 数月平稳
      用户感知 有时 502 / 卡顿 几乎无感
      系统负载 偶发高 恒定平稳

      💬 一句话总结:

      pm.max_requests 是让 PHP 长期高性能运行的“自愈机制”。
      它就像“自动换血”一样,让你的 PHP 进程永远保持新鲜活力。

    • #1272

      追光
      管理员

      ✅ Linux 的“空闲内存精简总结
      • Linux 的“空闲内存”主要看 Available(不是 Free)。
      • 安全范围:保持总内存的 15%~25% 可用。

      总内存 建议空闲(Available)
      2 GB ≥300 MB
      4 GB ≥500–800 MB
      8 GB ≥1–1.5 GB
      16 GB+ ≥10–15%

      🚀 小结
      可用内存低于 10% 时系统容易卡顿;
      20% 左右最理想——内存利用率高且留有安全余量。

正在查看 2 条回复
  • 在下方一键注册,登录后就可以回复啦。