先说结论:能跑,而且把这套组合调好了,它跑得比你想象中从容。这不是安慰,是我自己在2核2G的Linux云服务器上反复折腾、压测、踩坑之后得出的结论。很多刚接触云服务器的人一看到“2核2G”就觉得配置太低,生怕装上Nginx、MySQL、PHP之后直接卡死,其实这个配置在云服务器厂商那里通常是入门款,价格便宜,但对于个人博客、中小流量网站、小型API服务来说,它是性价比非常高的一个档位。核心问题从来不是“能不能跑”,而是“怎么装、怎么调、怎么用”,这三点搞明白,2核2G能发挥出的作用会远超你的预期。
这篇文章我会把整个链路讲透:三个组件各自吃掉多少资源、安装时怎么避开面板带来的额外负担、PHP-FPM和MySQL如何针对内存做参数级调优、Nginx在低配机器上应该承担什么角色,以及我实测出的真实承载能力。最后还会把我踩过的几个坑完整复盘一遍,包括SWAP抖动、进程莫名被杀、慢查询拖垮CPU这类典型问题。无论你用的是阿里云、腾讯云还是其他厂商的Linux云服务器,这套思路都可以直接套用。
1. 先给结论:能跑,而且比想象中更从容
1.1 为什么“2核2G”值得认真对待
很多人对2核2G的认知是被“大厂高配机器”惯出来的。你去查官方文档,MySQL动不动建议16G内存起步,PHP-FPM官方示例动辄几十个worker,Nginx的并发参数也写得特别激进。但这些配置是针对“一台服务器只跑一个偏重服务”的场景设计的,对于个人站长来说,一台2核2G的Linux云服务器同时跑Nginx、MySQL、PHP,本身就是一个非常经典的LNMP组合形态,它对应的不是大型分布式系统,而是“小团队够用、个人项目有余”的务实选择。
我个人的使用场景很典型:一个WordPress博客、一套自写的API服务、一个简单的图书管理系统Demo,三套业务共用一个数据库。运行了大半年,内存占用长期维持在60%到70%之间,CPU平时基本躺平,只有压测或爬虫扫的时候会冲高。这说明2核2G的瓶颈不在CPU,而在内存的精细化管理。CPU只有两个核心,但这个量级的业务计算量根本喂不饱它,真正需要盯紧的是内存别被某个组件的默认配置悄悄吃光。
1.2 “能跑”的三个判定标准
在聊调优之前,我建议先明确“能跑”的含义。如果你说的能跑是“页面能打开、接口能返回”,那2核2G闭着眼睛装都行;如果你说的能跑是“并发100下响应还在一秒内”,那就要认真做配置。我习惯用三个标准来评估一台低配服务器是否健康:
- 基础请求响应达标:WordPress页面首次访问在1.5秒内,带缓存后能进500毫秒,API接口响应在200毫秒内。
- 并发冲击下系统稳定:用ab或wrk压测时,内存不会瞬间被打满,PHP-FPM不会因为进程数爆掉而拒绝服务,MySQL不会大量出现慢查询。
- 日常运维有余量:SSH登录不卡顿,还能跑一些定时脚本或者日志分析任务,不会因为一个采集任务就把整台机器拖死。
这三个标准不是靠硬件堆出来的,而是靠合理的参数配置和业务侧缓存设计实现的。后面所有章节,本质上都是在回答“怎么达到这三个标准”。
2. 三件套的资源画像:谁吃内存、谁吃CPU、谁容易被低估
2.1 先看一份内存预算表
要把2核2G用好,第一步不是写配置,而是知道LNMP组合里每个角色的“饭量”到底多大。我基于Ubuntu 22.04的实际运行数据,整理了一份典型内存占用表,不同版本和不同业务下会有浮动,但量级基本一致:
| 组件 | 典型内存占用 | 说明 |
|---|---|---|
| 操作系统本身 | 300MB - 500MB | 主要是systemd、sshd、各种系统守护进程 |
| Nginx(master + worker) | 30MB - 80MB | 非常轻量,worker数量随CPU核数走 |
| PHP-FPM(每个worker进程) | 30MB - 60MB | 按请求动态创建,是内存波动的最大来源 |
| MySQL 8.0(InnoDB缓冲池128M时) | 250MB - 400MB | 固定成本高,启动后就会占住大部分缓冲池 |
| 其他辅助进程 | 50MB - 100MB | cron、日志轮转、监控agent等 |
从这张表能看出一个关键事实:操作系统加上MySQL的固定消耗,已经吃掉了大约700MB到900MB内存。剩下1.1GB到1.3GB,就是Nginx和PHP-FPM的舞台。Nginx非常克制,几十MB就够;真正的变量是PHP-FPM,每个worker根据业务逻辑不同可能吃30MB也能吃60MB,如果配置不当,几十个worker一拥而上,2G内存瞬间见底。理解了这一点,你就明白整篇文章的重心应该放在哪了——管好PHP-FPM,就管好了这台机器的大半内存。
2.2 PHP-FPM是动态消耗的大头
PHP-FPM的工作模式可以理解为“预启动一批worker进程,每个请求占用一个worker处理完再释放”。高并发下worker会被重复利用,但每个worker所占的内存不会在请求结束后完全释放,而是会保留一部分作为复用缓存,这也是为什么PHP-FPM的内存占用看起来只涨不降。
单个PHP-FPM进程的内存消耗和框架复杂度强相关。一个纯原生PHP接口可能只要20MB,但一个加载了完整Laravel或ThinkPHP框架的应用,单个worker的RSS(实际驻留内存)经常能到50MB甚至更高。如果按50MB算,开15个worker就是750MB,再叠加系统的900MB固定开销,2G内存基本就满了。所以后面我会详细讲怎么根据你的业务算出一个安全的max_children值,这是整个LNMP调优里最重要的一步。
2.3 MySQL是固定的成本中心
很多人以为MySQL不访问就不占资源,这是误解。MySQL只要启动,InnoDB缓冲池(innodb_buffer_pool_size)就会按配置预分配内存,即使一条查询都不跑,这块内存也已经“名花有主”了。这就是为什么2G内存机器上必须手动把InnoDB缓冲池压到合理水位,MySQL 8.0默认的128M其实还能接受,但如果你之前用过一些安装脚本,它给你设成512M甚至1G,那其他组件就没活路了。
除此之外,MySQL还有一些容易被忽略的隐性开销:线程缓存、表缓存、排序缓冲、连接缓冲,每一项单独看都不大,加起来却能轻松吃掉一两百MB。所以在低配机器上,MySQL的调优思路不是“跑得更快”,而是“少占地方、不留隐患”。
2.4 Nginx是这套组合里最省心的一个
Nginx在LNMP里的定位非常讨喜:它本身的内存开销极小,主进程加几个worker进程,总共一百MB以内就能跑得非常舒服。它的强项是事件驱动模型,一个worker可以同时处理成千上万个连接,CPU占用也极低。这就意味着你在2核2G机器上完全不需要给Nginx“省内存”,反而应该让它多承担一些工作,比如开启Gzip压缩、开启缓存、做反向代理,把压力从PHP和MySQL那边接过来。低配机器上的Nginx不是瓶颈,而是帮手。
3. 安装阶段的取舍:面板还是裸装,以及为什么我推荐裸装
3.1 面板的一句话优势与三条劣势
很多新手拿到云服务器第一件事就是装宝塔面板,确实方便,点几下就能把LNMP环境搭好。但如果你用的是2核2G这样的低配机器,我个人建议谨慎。
不是面板不好,而是它在你没注意的地方吃掉了稀缺资源。宝塔这类面板为了方便管理,会常驻多个守护进程、文件监控、计划任务、安全插件,安装完成后你打开系统监视器会发现多了三四百MB的额外占用。数据库管理工具、PHP扩展管理、防火墙管理这些功能虽然好用,但每一个背后都挂着一个常驻进程,这在2G内存上是实实在在的负担。
另外还有三条比较隐蔽的劣势:
- 面板默认的PHP-FPM参数和MySQL配置偏向“兼容性”而不是“针对性”,装完经常是MySQL缓冲池偏大、PHP worker数量激进,需要手动改的东西并不少。
- 面板的自动更新和安全体检会定时跑扫描任务,偶尔会在凌晨把CPU和内存占满,导致夜间告警。
- 如果面板本身被扫描到漏洞,你可能被迫进行一些额外修复工作;裸装环境暴露面小,反而更可控。
所以我的建议是:如果你的目标是长期稳定跑LNMP组合,又愿意花半小时敲几条命令,那就用裸装。裸装不仅省掉面板的内存开销,还能让你对每个组件都心里有数,出了问题知道去哪里查。如果实在需要图形界面,装完再考虑不迟,但别让它在低配机器上长期常驻。
3.2 裸装LNMP的正确顺序与关键操作
裸装LNMP的顺序并不复杂,以Ubuntu 22.04为例,最简单的操作是按Nginx、MySQL、PHP这个顺序安装。第一步先把系统软件源更新到最新,避免装到带漏洞的旧包:
sudo apt update && sudo apt upgrade -y然后一次性安装三个核心组件:
sudo apt install -y nginx mysql-server php-fpm php-mysql这条命令会同时把Nginx、MySQL 8.0、PHP(通常是8.1版本)装好。装完之后三个服务的启动状态可能各不相同,分别确认一下:
sudo systemctl status nginx sudo systemctl status mysql sudo systemctl status php8.1-fpm如果状态不是running,用systemctl start手动拉起来。这里有一个容易被忽略的点:PHP-FPM的版本号会随系统源变化,可能是php8.1-fpm,也可能是php8.3-fpm,用systemctl status时先通过php -v确认版本,再对应服务名操作。
接下来把PHP和Nginx串起来。默认的Nginx站点配置在 /etc/nginx/sites-available/default,在server块里加入PHP解析规则,让Nginx把.php请求转发给PHP-FPM处理。修改前建议先备份原文件:
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak然后用vim或nano编辑,在location块里加入这样一段:
location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.1-fpm.sock; }保存后测试配置并重载Nginx:
sudo nginx -t sudo systemctl reload nginx到这一步,LNMP的“骨架”已经搭好,可以在/var/www/html下放一个探针文件测试了:
<?php phpinfo();浏览器访问能看到PHP信息页,说明Nginx和PHP已经连通,MySQL通过php-mysql扩展也能在PHP代码里正常连接。
3.3 一个常被忽略的交换分区问题
低配机器上SWAP是绕不开的话题。很多教程会告诉你“加2G SWAP”,但我的实测经验是,2G内存的机器加1G SWAP就够,而且不能只关注有没有,还要关注它在高负载时会不会添乱。
创建SWAP的标准操作是把一个文件当作虚拟内存使用:
sudo fallocate -l 1G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile为了重启后依然生效,还要写入/etc/fstab:
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab这里有个非常重要的经验:创建SWAP之后,一定要检查系统的swappiness值。Linux默认的vm.swappiness通常是60,意思是内存使用率超过一定阈值后,系统会倾向于把不常用的内存页换到SWAP里。但在2G内存这样的小内存机器上,swappiness过高的直接表现是:明明物理内存还有剩余,系统却开始用SWAP,导致响应变慢、IO升高。
把swappiness调低,让系统优先留在物理内存:
sudo sysctl vm.swappiness=10 echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf这样SWAP只在内存真正吃紧时兜底,而不是日常“自动加速”拖慢整台机器。
4. PHP-FPM进程数怎么算:从内存预算反推Worker配置
4.1 max_children的核心公式
PHP-FPM调优最核心的参数是pm.max_children,它决定了最多能同时跑多少个worker进程。设大了,并发能力高,但内存容易爆;设小了,内存安全,但并发稍微一高就会排队甚至502。
这个值的计算方式不复杂,本质是一个除法:可用内存除以单个PHP-FPM进程的平均内存占用。可用内存要从总内存里先减去操作系统、MySQL、Nginx以及其他常驻进程的部分,还要留出一部分安全冗余,防止流量突刺时OOM。公式写出来就是:
max_children = (总内存 - 系统内存 - MySQL内存 - Nginx内存 - 业务冗余) / 单个PHP-FPM进程内存这个公式看起来简单,但真正决定参数是否合理的,是“单个PHP-FPM进程内存”这个分母怎么估。我建议不要拍脑袋,先用默认配置跑一段时间,然后用命令实测:
ps -ylC php-fpm --sort:rss这条命令会列出所有PHP-FPM进程,并按照内存从大到小排列,RSS列就是每个进程的实际驻留内存。用这个数据算出来的分母才是你当前业务环境的真实值。
4.2 用2G内存做一遍具体推算
我以自己那台2核2G云服务器的实际数据为例,演示一遍完整推算过程。
先说各项基础占用:系统自身约400MB,MySQL按128M缓冲池配置后约350MB,Nginx约50MB,其他辅助进程约50MB。2G内存换算成MB是2048MB,减去上面四部分,2048减850等于1198MB,这就是PHP-FPM可用的初始预算。但我不可能把这1198MB全部用光,还要留出300MB给可能的流量突刺和系统缓存,那么PHP-FPM实际可用就是898MB。
我的WordPress站点实测下来,单个PHP-FPM进程平均在45MB左右,那么:
max_children = 898 / 45 ≈ 19看起来最多可以设到19个worker。但我要提醒一句:这个19是理论极限,不是推荐值。实际跑起来我会先设成10到12个,观察一段时间内存曲线,如果稳定再逐步往上加。因为装机初期你会遇到采集机器人、攻击扫描、插件波动等各种意外流量,留出安全余量比追求极限并发重要得多。
4.3 动态模式 vs 静态模式的取舍
PHP-FPM的进程管理模式有dynamic、static、ondemand三种。在2核2G机器上,我强烈建议使用dynamic模式,让进程数根据实际流量自动调整,而不是固定占满内存。
具体配置在 /etc/php/8.1/fpm/pool.d/www.conf 里,核心参数如下:
pm = dynamic pm.max_children = 10 pm.start_servers = 3 pm.min_spare_servers = 2 pm.max_spare_servers = 6 pm.max_requests = 500这些参数的含义分别是:
- pm.start_servers:启动时预创建的worker数量,3个够用,避免空转。
- pm.min_spare_servers:空闲worker下限,保持在2个,应对突发请求不用现起进程。
- pm.max_spare_servers:空闲worker上限,达到6个后不再新增,防止无人访问时白白占内存。
- pm.max_requests:每个worker处理500个请求后自动重启,这是用来解决PHP内存泄漏的,避免单个worker长期运行后内存涨成天文数字。
修改完配置后记得重启PHP-FPM:
sudo systemctl restart php8.1-fpm重启后再用ps命令观察进程数和内存占用,确认配置生效。这里的逻辑是:宁可让少量请求排队,也别让几十个worker同时把内存打爆。对个人网站来说,几毫秒的排队完全感知不到,但OOM宕机却是实打实的灾难。
5. MySQL瘦身:把InnoDB缓冲池压到合理水位
5.1 为什么默认配置不适合2G机器
MySQL 8.0装好之后,默认配置针对的是通用场景,它假设你的机器至少有4G到8G内存。在2G机器上,最需要动手术的就是InnoDB缓冲池。这个参数决定了MySQL在内存里缓存多少数据和索引,设得越大,查询越快,但占用的内存也越夸张。
MySQL 8.0的默认innodb_buffer_pool_size是128M,单看不算大。但问题在于MySQL组件不止这一个内存消耗点,InnoDB还有日志缓冲、数据字典、锁信息等一堆附属结构;Server层还有线程缓存、表缓存、排序缓冲、临时表内存堆。这些加在一起,即使缓冲池只有128M,MySQL的整体内存占用也会爬到300M以上。
很多一键安装脚本为了让数据库“性能更好”,会把innodb_buffer_pool_size改到512M甚至1G,这在小内存机器上就是灾难。所以我每次在2G机器上装完MySQL,第一件事就是检查这个值,确认它没有被人为调大:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';5.2 一组适合2核2G的MySQL配置清单
结合我自己小半年运行下来的体验,下面这份配置在2核2G机器上比较稳妥。编辑 /etc/mysql/mysql.conf.d/mysqld.cnf,在 [mysqld] 段下添加:
[mysqld] innodb_buffer_pool_size = 128M innodb_log_file_size = 64M innodb_flush_log_at_trx_commit = 2 max_connections = 100 performance_schema = OFF skip-name-resolve = ON逐条解释一下为什么这么设:
- innodb_buffer_pool_size = 128M:这是MySQL最大的内存消耗点,128M在2G机器上是安全线,个人站的数据量和索引完全够缓存。
- innodb_log_file_size = 64M:重做日志文件大小,64M够日常写入,改太大反而占磁盘和内存。
- innodb_flush_log_at_trx_commit = 2:这个参数控制事务日志的刷盘时机。设为2表示每次事务提交只写入操作系统缓存,每秒刷一次磁盘,能明显降低磁盘IO,代价是极端断电情况下可能丢失最多一秒的事务。对个人网站来说完全能接受,但如果你跑的是支付、订单这类金融业务,必须保持默认的1。
- max_connections = 100:2G机器上MySQL能同时处理的连接数有限,100是安全上限。每个MySQL连接会占用线程栈和缓存,设1000个连接其实没有意义,PHP-FPM的worker数量才10个,根本用不到那么多连接。
- performance_schema = OFF:这个参数很多人不知道,它是MySQL的性能监控模块,会持续采集大量运行数据,内存开销在100MB以上。对个人服务器来说,关了能省不少内存,对性能几乎没有负面影响。
- skip-name-resolve = ON:关闭反向DNS解析,每次连接建立时少一次DNS查询,既省时间又省资源。如果你的应用通过域名连接数据库,关闭前要确认权限表里用的是IP而不是主机名。
改完配置重启MySQL:
sudo systemctl restart mysql重启后可以用这个SQL检查关键参数是否生效:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections';5.3 用慢查询日志确认没有隐藏问题
调完参数不是终点,还要确认业务侧没有“慢性毒药”。2核2G机器最怕的不是配置高,而是SQL写得差,一个全表扫描就能把CPU打满。开启慢查询日志是我每次部署后必做的一步:
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1;含义是记录所有执行时间超过1秒的SQL。跑几天后查看 /var/log/mysql/mysql-slow.log,如果发现某个查询频繁出现,就要考虑加索引或者优化业务逻辑。观察一段时间确认没有慢查询之后,建议关掉这个日志,因为慢查询日志本身也会持续写入磁盘,在低配机器上没必要长期开着。
6. Nginx的角色定位:它是这套架构里最省心的那个
6.1 worker进程与连接数的基础配置
Nginx在2核2G机器上的调优空间不大,但有两个参数值得看一眼。打开 /etc/nginx/nginx.conf,在events块和http块里确认一下:
worker_processes auto; events { worker_connections 1024; }worker_processes设成auto后,Nginx会根据CPU核心数自动启动对应数量的worker进程,2核机器就是2个worker。worker_connections表示每个worker最多能同时处理1024个连接,两个worker加起来就是2048,对个人站来说绰绰有余。这两个参数基本不需要动,Nginx的默认值就是为了这种场景准备的。
6.2 开启Gzip与HTTP/2,让流量更省、体验更好
Nginx在LNMP里最有价值的工作是“减负”。对PHP动态页面来说,开启Gzip压缩能让传输体积下降60%以上,用户等待时间明显变短;对静态资源来说,Nginx可以直接接管,完全不经过PHP。
在nginx.conf的http块里加这段Gzip配置:
gzip on; gzip_comp_level 5; gzip_min_length 1k; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss image/svg+xml;gzip_comp_level不建议设太高,5是一个兼顾压缩率和CPU消耗的折中点,设成9在低配机器上反而浪费CPU。另外如果站点启用了HTTPS,建议顺便打开HTTP/2:
listen 443 ssl http2;HTTP/2能在一个连接上多路复用多个请求,对减少浏览器并发连接数和页面加载时间都有明显帮助。前提是你的Nginx版本支持,Ubuntu 22.04的Nginx默认就支持,直接用就行。
6.3 反向代理场景下需要注意的细节
如果你不只是跑LNMP,而是想让Nginx同时代理后端的Node.js或Python服务,不要直接套别人的大内存配置。Nginx反向代理默认会给每个上游连接分配缓冲,proxy_buffer_size默认是4k到8k,如果后端的接口经常返回体积较大的响应,你可以根据实际情况适当微调,但千万别为一个接口设置几MB的代理缓冲,那在2G机器上是非常奢侈的。
我的做法是只设置必要的最小缓冲:
proxy_buffering on; proxy_buffer_size 8k; proxy_buffers 8 8k;这样既能保证上游响应能正常缓冲,又不会因为过度分配而压垮内存。总之一句话:Nginx在低配机器上应该保持“轻巧但多功能”,而不是把所有内存都吃进代理缓冲里。
7. 真实场景实测:2核2G跑WordPress,到底能扛住多少流量?
7.1 测试环境与方法
理论说了这么多,最后还是得看真实数据。我的测试环境是一台2核2G的Linux云服务器,Ubuntu 22.04系统,Nginx 1.18,PHP 8.1,MySQL 8.0,跑的是WordPress博客,安装了一个页面缓存插件。压测工具用的是Apache自带的ab,这种方式最简单也最直观,不需要额外装复杂工具。
测试分两层:一层是未启用页面缓存时的动态请求,一层是启用缓存后的请求。因为WordPress的PHP渲染开销很大,未缓存时要走完整的PHP执行链路,缓存后Nginx直接返回静态HTML,代表两种完全不同量级的压力。
7.2 实测数据实测数据
未启用缓存的情况下,用ab模拟10个并发、总共1000个请求:
ab -c 10 -n 1000 http://你的域名/结果大概是每秒处理200到300个请求,平均响应时间在300到500毫秒左右。这个数据对动态页面来说已经够用了,10个并发同时访问首页,用户几乎感知不到延迟。内存方面,PHP-FPM的worker进程数保持在6到8个,内存利用率在60%到70%之间,比较健康。
启用页面缓存之后,同样的并发参数打过去,每秒可以处理1500个以上请求,平均响应时间掉到几十毫秒。因为这时候Nginx直接读缓存文件返回,完全不经过PHP和MySQL,CPU占用率也大幅下降。这说明在2核2G机器上,流量承载能力的上限在很大程度上取决于你的缓存策略。
把并发提高到50、总请求数5000,未缓存场景下响应时间会上升明显,PHP-FPM开始排队,偶尔还会出现502错误;但启用缓存后依然稳如磐石。这个结果让我对2核2G的信心又高了一层——只要前端缓存做好,它扛住日PV几万的个人站没有压力。
7.3 这个结论的边界条件
上面的数据只是参考,不是标准答案。你的站点如果用的是Laravel这种大框架,跑的又是一堆复杂的数据库查询,性能数据会低很多;如果是个纯静态展示站,数据又会高很多。所以核心不是纠结具体数字,而是理解一个规律:2核2G的瓶颈顺序是内存优先于CPU,动态请求优先于静态请求,数据库查询优先于普通逻辑判断。顺着这个优先级去设计缓存和索引,这台机器能发挥出的能力远超纸面参数。
另外提醒一句,云服务器的性能还受隔壁邻居影响,也就是所谓“突发性能”和“基准性能”的区别。如果厂商标注了基准性能偏低,跑压测时数据可能比我的结果还低,这是正常现象,不必焦虑。
8. 一定会踩的坑:SWAP抖动、OOM杀进程与慢查询拖垮CPU
8.1 SWAP不是越多越好
我在3.3节提到创建1G SWAP,并把swappiness调低到10。这里展开讲一下如果不调,你会看到什么现象。
默认swappiness为60时,系统的内存回收机制相当积极。你登录服务器执行free -h,会看到SWAP这一栏已经有几百MB被占用,但物理内存明明还有空闲。这时候系统行为会变得很奇怪:明明内存够用,磁盘却一直在做换入换出,整机响应变慢,尤其当云盘IO本身性能一般时,卡顿感会非常明显。
调低swappiness之后,系统会优先使用物理内存,只有内存真正吃紧才动用SWAP。我自己调完之后,日常运行SWAP占用长期为0,只有压测高峰才偶尔用几十MB,说明这个策略是合理的。这里再提示一下,SWAP的坑不在于“有”,而在于“时机不对”。
8.2 进程莫名被杀:先看dmesg和系统日志
2G内存机器上最典型的故障是MySQL或者PHP-FPM进程突然消失,日志里什么异常都没有,服务状态变成inactive或failed。新手容易以为是软件崩溃,其实大多数情况是Linux内核的OOM Killer根据内存压力挑了一个“最肥”的进程杀掉,用来释放内存。
排查方法非常简单,执行:
sudo dmesg | grep -i oom或者查看系统日志:
sudo grep -i oom /var/log/syslog你会看到类似“Out of memory: Killed process 1234 (mysqld)”的记录,这就实锤了是内存不足导致OOM。我遇到过最典型的一次,是我图省事把PHP-FPM的max_children设成了20,结果某个爬虫深夜扫站,所有worker同时跑起来,加上MySQL、系统开销,2G内存瞬间打满,MySQL被内核优先杀掉,整站数据库连接全部失败。
复盘后我把max_children调回10,并把MySQL的performance_schema关掉,内存水位一下就降下来了,之后再没出现过OOM。
8.3 一次排查实例:内存告警后的完整链路
我整理一下遇到OOM告警时的完整排查顺序,这个思路适用于任何低配Linux云服务器:
# 第一步:确认物理内存和SWAP现状 free -h # 第二步:按内存占用从大到小列出所有进程 ps aux --sort=-%mem | head -20 # 第三步:确认是否发生过OOM杀进程 dmesg | grep -i oom # 第四步:查看PHP-FPM实际worker数量和内存 ps -ylC php-fpm --sort:rss # 第五步:查看MySQL关键内存配置 mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'performance_schema';"这五步走完,基本能定位是哪个环节吃掉了内存。我建议内存使用长时间超过80%就触发告警,不要等OOM发生再补救。云厂商自带的基础监控一般都有内存监控,按过80%告警设好,能给你留出足够的响应时间。
8.4 别忘了云厂商的监控告警
最后聊一个很多人忽略的点:2核2G机器一定要开云厂商自带的基础监控告警。阿里云、腾讯云这些厂商的控制台都提供免费的CPU、内存、磁盘监控,你花两分钟设一条内存超过80%就通知你的规则,能在问题发生前就介入,比事后翻日志高效得多。
这里我要强调一个容易踩的坑:云监控里看到的内存使用率,通常包含IO缓存(Buff/Cache),而Linux系统为了保证性能,会尽量把空闲内存用作缓存。所以监控显示80%使用率,实际可用内存可能还有余量。但反过来,如果监控持续超过90%,那基本就是真的吃紧了。我自己的习惯是,同时看监控的“内存使用率”和“SWAP使用率”,如果这两个都高,说明内存确实告急,需要优化配置或考虑升配了。
折腾这一圈下来,我个人最深的体会是:2核2G这套配置的意义不在于“能省多少钱”,而在于它逼着你把每一个组件都搞清楚。装面板谁都会,默认参数谁都会用,但只有当你手动算过PHP-FPM的max_children、亲手调过MySQL的缓冲池、观察过SWAP的变化曲线,你才算真正理解这台Linux云服务器是怎么运转的。我现在已经把调优后的配置备份成脚本,换新服务器时十分钟就能复现一套稳妥的LNMP环境。这个过程踩过的每一个坑,都是后面受益的基础。