13 10 月 2026

"网站好慢"是最模糊的故障描述。慢可能发生在任何一环:本地网络、DNS、服务器带宽、Nginx、PHP、数据库、前端资源、第三方接口……

这篇给一套自顶向下的定位流程,每一步都有可执行命令,帮你把"慢"变成具体的数字和具体的环节。

排到最后如果是带宽或配置瓶颈,就该升级了。财富云计算 提供云服务器与带宽升级选项;内网服务、游戏服务端可选 NAT 型(无独立公网 IP,价格比独立 IP 机型省一半以上)。

一、先量化:到底慢在哪一步

用 curl 分解耗时

curl -o /dev/null -s -w '
DNS 解析:   %{time_namelookup}s
TCP 连接:   %{time_connect}s
TLS 握手:   %{time_appconnect}s
开始传输:   %{time_starttransfer}s  ← 首字节时间(TTFB)
总耗时:     %{time_total}s
下载速度:   %{speed_download} B/s
HTTP 状态:  %{http_code}
' https://example.com

输出示例:

DNS 解析:   0.012s
TCP 连接:   0.145s
TLS 握手:   0.302s
开始传输:   1.850s   ← TTFB 很高,问题在服务端
总耗时:     1.920s
下载速度:   125000 B/s
HTTP 状态:  200

怎么读:

指标偏高说明
time_namelookup 大DNS 慢,换 DNS 或检查解析
time_connect 大网络链路差、跨运营商、服务器远
time_appconnect 大TLS 握手慢,检查证书链、OCSP
time_starttransfer(TTFB)大服务端处理慢(PHP/数据库/程序逻辑)
time_total - time_starttransfer 大内容下载慢(大文件、带宽小)
speed_download 小带宽瓶颈

经验阈值(国内访问):

  • DNS < 50ms
  • TCP < 100ms(同城)/ < 200ms(跨省)
  • TTFB < 500ms 算不错,> 2s 明显有问题
  • 首屏加载 < 2s

用浏览器开发者工具

F12 → Network → 刷新页面:

  • 哪个请求最慢(瀑布图一眼看出)
  • Waterfall 各阶段:Queueing / Stalled / DNS / SSL / Request sent / Waiting(TTFB) / Content Download
  • Waiting 长 = 服务端慢
  • Content Download 长 = 资源大或带宽慢
  • Stalled 长 = 浏览器并发限制或连接排队

二、排除本地因素

先确认不是你自己网络的问题:

# 1. 换个网络(手机 4G)试试
# 2. 多地 ping
ping example.com -c 10

# 3. 看网络链路哪一跳慢
mtr example.com
traceroute example.com

# 4. 换 DNS 对比
curl --resolve example.com:443:1.2.3.4 https://example.com -o /dev/null -w "%{time_starttransfer}\n"

在线多地测速:

如果只有你慢、其他地方正常 → 是本地网络问题,不用折腾服务器。


三、服务端排查:按层拆

第 1 层:服务器资源

uptime        # 负载
free -h       # 内存
df -h         # 磁盘
top           # 谁在吃资源
iostat -x 1   # 磁盘 IO
iftop         # 带宽

详细排查见本站《服务器资源排查:CPU / 内存 / 磁盘 / 带宽》。

快速判断:

  • 负载高 + wa 高 → 磁盘 IO 瓶颈(数据库在拼命读盘)
  • 内存 available 接近 0 + swap 在用 → 内存瓶颈,会非常慢
  • 带宽跑满 → 看是不是被盗链或被攻击

第 2 层:Nginx

看访问日志里哪些请求慢:

先改日志格式加上耗时(在 nginx.conf 的 http 块):

log_format timed '$remote_addr [$time_local] "$request" $status '
                 '$body_bytes_sent rt=$request_time urt=$upstream_response_time '
                 '"$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log timed;
nginx -t && systemctl reload nginx

找出最慢的请求:

# 按 $request_time 排序(awk 取对应字段,按你的日志格式调整)
awk '{print $NF, $0}' /var/log/nginx/access.log | sort -rn | head -20

检查 Nginx 状态:

systemctl status nginx
tail -100 /var/log/nginx/error.log

常见:

  • worker_connections are not enough → 调大
  • upstream timed out → 后端慢
  • connect() failed → 后端挂了

第 3 层:PHP-FPM(动态站点的头号嫌疑)

开启慢日志:

nano /etc/php/8.2/fpm/pool.d/www.conf
slowlog = /var/log/php-fpm/slow.log
request_slowlog_timeout = 2s
mkdir -p /var/log/php-fpm
systemctl restart php8.2-fpm
tail -f /var/log/php-fpm/slow.log

慢日志会打印出具体哪个 PHP 文件的哪一行慢,直接定位到代码。

检查 FPM 是否在排队:

# 开启状态页
# www.conf 里:pm.status_path = /phpfpm-status
# Nginx 里配一个 location 访问它
curl http://127.0.0.1/phpfpm-status

关注:

listen queue: 0          ← 大于 0 说明请求在排队,进程不够
active processes: 5
max active processes: 10 ← 经常打满说明要加进程
max children reached: 3  ← 大于 0 说明进程数不够,请求被拒绝
slow requests: 12        ← 慢请求数

listen queue 或 max children reached 大于 0 → 调大 pm.max_children(注意内存够不够)。

OPcache 一定要开:

php -m | grep -i opcache     # 看看有没有
apt install php-opcache -y

没有 OPcache 的 PHP,每次请求都要重新编译脚本,性能差好几倍。

第 4 层:数据库

MySQL 慢查询日志:

nano /etc/mysql/mysql.conf.d/mysqld.cnf
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1          # 超过 1 秒记录
log_queries_not_using_indexes = 1
systemctl restart mysql

分析:

# 直接看
tail -100 /var/log/mysql/slow.log

# 用 mysqldumpslow 汇总
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log     # 最耗时的 10 条
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log     # 出现最多的 10 条

# 或用 pt-query-digest(更专业)
pt-query-digest /var/log/mysql/slow.log

实时看正在执行的慢 SQL:

SHOW PROCESSLIST;
-- 或
SELECT * FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep' AND TIME > 2 ORDER BY TIME DESC;

最常见的数据库慢原因:

  1. 没加索引 → EXPLAIN SELECT ... 看是否全表扫描
  2. 索引失效(函数包裹、LIKE '%xx'、类型不匹配、隐式转换)
  3. 查询返回太多行(没分页、SELECT *)
  4. 联表过多或写法糟糕
  5. 锁等待(长事务、MyISAM 表锁)
  6. buffer pool 太小(innodb_buffer_pool_size,建议设为内存的 50~70%)
-- 加索引
ALTER TABLE posts ADD INDEX idx_created (created_at);

-- 看表状态
SHOW TABLE STATUS;
-- 看索引
SHOW INDEX FROM posts;

第 5 层:外部依赖

你的程序可能调用了第三方 API(支付、地图、短信、统计),它们慢会拖垮整站。

# 测试第三方接口耗时
curl -o /dev/null -s -w "%{time_total}\n" https://api.third.com/xxx

# 看服务器上的外部连接
ss -tnp | grep -v "127.0.0.1"

解决:加超时(别无限等待)、加缓存、异步化。


四、前端层面(资源大、请求多)

服务端 TTFB 很快,但页面还是要很久才出来 → 是前端问题。

检查清单

  • [ ] 图片有没有压缩(一张 3MB 的 PNG 就是几秒)
  • [ ] 图片格式(用 WebP,体积能小 30~70%)
  • [ ] 有没有开启 Gzip / Brotli
  • [ ] JS/CSS 有没有合并压缩
  • [ ] 有没有加载不必要的第三方脚本(统计、广告、社交插件)
  • [ ] 字体文件是否过大(中文字体子集化很重要)
  • [ ] 有没有用 CDN
  • [ ] 资源有没有缓存头

实操

# 检查是否开启压缩
curl -H "Accept-Encoding: gzip" -I https://example.com | grep -i "content-encoding"

# 检查缓存头
curl -I https://example.com/style.css | grep -i cache

# 看资源大小
curl -o /dev/null -s -w "%{size_download}\n" https://example.com/big.jpg

Nginx 开启缓存头:

location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

图片优化工具:

# 批量压缩 jpg
apt install jpegoptim -y
jpegoptim --max=85 *.jpg

# 批量压缩 png
apt install optipng -y
optipng -o2 *.png

# 转 webp
apt install webp -y
cwebp -q 80 input.jpg -o output.webp

五、网络与带宽层

检查带宽是否跑满

iftop -i eth0
sar -n DEV 1
# 统计当天流量
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20
# 看哪个 IP 请求最多、哪个资源流量最大

异常情况:

现象原因处理
某个 IP 请求量巨大CC 攻击 / 爬虫封 IP、限速
图片被盗链Referer 异常加防盗链
大文件被反复下载被盗链/没缓存CDN、限速
无异常但带宽满了真流量大升级带宽

限速与防盗链配置:

# 防盗链
location ~* \.(jpg|png|gif|mp4)$ {
    valid_referers none blocked server_names *.example.com;
    if ($invalid_referer) { return 403; }
}

# 单连接限速
location /download/ {
    limit_rate 500k;
}

# 限制单 IP 并发与请求频率(防 CC)
limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

server {
    limit_req zone=req_limit burst=20 nodelay;
    limit_conn conn_limit 20;
}

六、DNS 层

dig example.com +short
dig example.com | grep "Query time"

Query time 超过 100ms 就偏慢。

优化:

  • 换更快的 DNS 服务商(DNSPod、阿里云 DNS、Cloudflare)
  • TTL 设大一些(3600+),减少查询次数
  • 如果用户遍布全国/全球,用支持分线路解析的 DNS(电信走电信 IP、联通走联通 IP)
  • 检查是否有 CNAME 链过长

DNS 预解析(前端优化):

<link rel="dns-prefetch" href="//cdn.example.com">

七、性能优化的常规手段(按性价比排序)

手段效果成本
开启 OPcache⭐⭐⭐⭐⭐装个包
开启 Gzip/Brotli⭐⭐⭐⭐几行配置
数据库加索引⭐⭐⭐⭐⭐一条 SQL
静态资源缓存头⭐⭐⭐几行配置
图片压缩 + WebP⭐⭐⭐⭐批量工具
页面缓存(Nginx proxy_cache)⭐⭐⭐⭐⭐中等
Redis/Memcached 缓存查询结果⭐⭐⭐⭐中等
CDN 加速⭐⭐⭐⭐按流量付费
升级服务器配置⭐⭐⭐花钱,最直接
前后端分离 / 静态化⭐⭐⭐⭐重构成本高

最推荐先做的三件事:OPcache、数据库索引、静态资源缓存。投入极小,收益极大。


八、排查流程图

网站慢
 │
 ├─ curl -w 分解耗时
 │    ├─ DNS 慢 → 换 DNS
 │    ├─ 连接慢 → 网络链路 / 换机房 / 上 CDN
 │    ├─ TTFB 慢 → 服务端问题 ↓
 │    └─ 下载慢 → 资源太大 / 带宽小 → 压缩 + CDN
 │
 └─ TTFB 慢(服务端)
      ├─ top / free / iostat → 资源瓶颈?
      │    ├─ CPU 高 → 查进程 → 程序问题或攻击
      │    ├─ 内存满 → 加内存或限流
      │    ├─ IO 高 → 数据库在疯狂读盘 → 加索引/加内存
      │    └─ 带宽满 → 查流量来源
      ├─ PHP 慢日志 → 定位到具体文件行
      ├─ FPM 状态页 → 进程不够?调 pm.max_children
      ├─ MySQL 慢日志 → 定位慢 SQL → EXPLAIN → 加索引
      ├─ Nginx error.log → 后端超时?502/504?
      └─ 第三方 API → 加超时/缓存

九、建立监控,别等用户说

# 简单的定时探测(记录 TTFB)
*/5 * * * * curl -o /dev/null -s -w "%{http_code} %{time_starttransfer}\n" https://example.com >> /var/log/site-perf.log

更完整的方案见本站《游戏服务器监控与告警实战》,Netdata 一行命令就能装好,能实时看到所有指标。

告警阈值建议:

  • TTFB > 2s 持续 5 分钟 → 告警
  • HTTP 状态码非 200 → 立即告警
  • 磁盘 > 85% → 告警
  • 带宽 > 80% → 告警

排查到最后如果是带宽或配置瓶颈,就该考虑升级了。财富云计算 的云服务器支持按需升级 CPU、内存与带宽,也可以先做快照再升级,风险可控。做内网服务、测试环境、游戏服务端推荐 NAT 型(无独立公网 IP,价格比带独立 IP 机型省一半以上),省下的预算投到内存和带宽上更实在。

相关阅读:

  • 《服务器资源排查:CPU / 内存 / 磁盘 / 带宽》
  • 《Nginx 反向代理与多站点配置》
  • 《服务器备份与快照策略》
延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:用云服务器搭建 Typecho 博客:LNMP 环境从零到上线
  2. 下一篇:Linux 防火墙配置实战:ufw / firewalld / iptables 该怎么选
发表评论 3