你刷新页面,盯着浏览器左下角那个“正在连接…”的提示,心里默默数到三——页面还没出来。这时候别急着怪CDN、别翻前端代码,先问问自己:服务器是不是在那儿发呆?

TTFB(首字节时间)卡在1秒以上?那不是网络问题,是你的服务器正在手忙脚乱地处理请求。图片压了、JS合了、CDN也上了,可它还是慢——说明问题不在路上,而在起点。

你的服务器到底卡在哪里?先查这3个指标

很多人一说优化,第一反应就是“换服务器”或者“加带宽”。其实多数时候,服务器硬件完全够用,只是配置没跟上流量节奏。

盯住这三个数字就够了:
TTFB(首字节时间):超过500ms,基本可以确定是服务端响应拖了后腿;
并发连接数:如果快摸到上限,新来的请求只能干等;
CPU空闲率:长期低于30%,说明服务器不是在干活,是在硬扛。

真实案例:一个电商站,TTFB一直稳定在1秒出头,前端能做的都做了。上去一看,Apache的MaxClients还卡在默认的150,而实际高峰并发早就冲到300+。后面一半请求排着队等,TTFB当然下不来。调高之后,TTFB直接掉了一半。

为什么你的数据库查询比蜗牛还慢?3个排查方向

服务器慢,八成是数据库在拖后腿。别急着升级硬盘或加内存,先看看SQL有没有“踩坑”。

方向一:打开慢查询日志。MySQL默认不记慢查询,得手动开。设个阈值(比如1秒),跑一两天,直接看到哪些SQL在拖后腿。之前帮一个博客排查,首页加载慢,结果发现一条文章列表查询跑了快4秒——因为没建索引,每次都要扫几十万行。

方向二:检查关键字段有没有索引WHERE status = 1 AND created_at > '2023-01-01'这种查询,如果statuscreated_at都没索引,数据库只能全表扫描。加个复合索引,查询从秒级掉到毫秒级很常见。

方向三:别让每次请求都重连数据库。PHP-FPM默认每次请求都新建MySQL连接,高并发时光握手就耗掉不少时间。换成持久连接,或者用系统自带的连接池机制(比如MySQL 8.0+的mysqlx或PHP的PDO::ATTR_PERSISTENT),数据库响应能快不少。

真实案例:一个论坛晚上总卡顿,查慢查询日志发现一条SELECT * FROM posts ORDER BY RAND() LIMIT 10,每次执行两秒多。ORDER BY RAND()会让MySQL给每一行算随机值再排序,代价极高。改成预生成随机ID再JOIN,查询时间降到0.02秒,TTFB也跟着明显下降。

缓存到底该放哪里?4个层级一次说清

缓存不是“装个Redis”就完事了。它是一道防线,得一层一层布防,每层解决不同问题。

第一层:OPcache(PHP操作码缓存)。PHP每次请求都要把代码编译成操作码,OPcache就是把编译结果存在内存里。没开它,等于每次都在重复编译。检查phpinfo()里OPcache是否启用,opcache.memory_consumption建议至少128MB。

第二层:对象缓存(Redis/Memcached)。用户Session、商品详情、配置项这类读多写少的数据,扔进Redis缓存几分钟,数据库压力立马轻下来。注意设好过期时间,别让旧数据赖着不走。

第三层:页面缓存(Nginx/Varnish)。对不需要登录态的页面(比如首页、文章页、产品列表),让Nginx直接返回静态HTML,连PHP都不启动。很多团队只加这一层,TTFB就从几百毫秒掉到十几毫秒。

第四层:浏览器缓存(Cache-Control/ETag)。通过Cache-Control: max-age=31536000这类响应头,让浏览器把CSS、JS、图标存在本地。下次访问直接读硬盘,连请求都省了。但更新资源时记得改文件名或加版本号,不然用户永远看不到新样式。

真实案例:一个新闻站,TTFB原来在800ms左右。只在Nginx里加了页面缓存(首页缓存5分钟),TTFB立刻掉到15ms——因为Nginx根本没转发给PHP,直接吐HTML。

你的Web服务器配置是不是在偷懒?3个关键参数

Apache和Nginx的默认配置,是为小博客准备的。流量上来后,不调参就像让自行车拉货柜。

参数一:KeepAlive Timeout。KeepAlive本身是好事,但超时设太长(比如默认5秒),连接会空占资源。建议改成2–3秒,既保证复用,又不浪费。

参数二:Worker进程与连接数。Nginx里worker_processes一般设成CPU核心数,worker_connections至少1024。如果并发稍高就报“connection refused”,大概率是这两个值太保守。

参数三:PHP-FPM的pm.max_children。这个值决定最多同时处理几个PHP请求。设小了,请求排队;设大了,内存爆掉。经验是:按每个PHP进程占30–50MB估算,比如8GB内存的服务器,max_children设到150–200比较稳。

真实案例:一个SaaS后台下午三点准时变慢。查监控发现,PHP-FPM的max_children只设了50,而高峰期并发常达80+。多出来的请求全在队列里等,TTFB一路飙到1秒以上。调到120后,TTFB稳在200ms内。

动静分离真的能救你吗?2个落地步骤

动静分离不是“把图片传到CDN”就结束了。核心是:让静态资源彻底绕过你的应用服务器。

第一步:用独立子域名托管静态资源。比如把/css/app.css改成https://static.yoursite.com/css/app.css,然后在Nginx里配好这个域名,让它直接读文件、不走PHP。这样既能减少主站压力,又能突破浏览器单域名并发限制。

第二步:让CDN真正接管静态请求。CDN不只是加速,更是分流器。活动期间图片、JS、字体请求暴增,如果全打到源站,TTFB必然受影响。把静态资源全部指向CDN地址,源站只管动态内容,压力自然小一大截。

真实案例:一个在线教育平台,课程列表页要加载几十张缩略图,全放在主站。结果图片请求挤占PHP进程,TTFB从500ms涨到1.2秒。迁移到CDN后,TTFB回到300ms左右,页面加载明显更顺。

今天就能做的1个操作:打开MySQL慢查询日志

别等用户投诉了再动手。你现在就打开终端,连上服务器,执行这三步:

  1. 进入MySQL:mysql -u root -p
  2. 开启日志:SET GLOBAL slow_query_log = 1;
  3. 设阈值为1秒:SET GLOBAL long_query_time = 1;

日志默认会写到/var/log/mysql/slow.log(路径可通过SHOW VARIABLES LIKE 'slow_query_log_file';确认)。跑一天后,用mysqldumpslow /var/log/mysql/slow.log | head -20看最慢的20条SQL。
找到它们,加索引、删SELECT *、避开ORDER BY RAND()——就这一个动作,TTFB很可能明天就开始往下掉。
现在就去敲命令,别等下班。