网站打开慢,很多人第一反应是"带宽不够"或者"图片太大",但真正卡住用户的那段时间,往往不在前端。你在浏览器里输入网址到页面开始出现内容,中间有一段时间是浏览器在等服务端回话,这段时间就叫 TTFB(Time To First Byte,首字节时间)。TTFB 高,意味着服务器"反应慢",后面再怎么优化图片和脚本都是治标。
这篇文章要解决的就是:TTFB 到底是什么,由哪几段组成,怎么用命令量化检测,以及从后端处理、缓存、数据库、连接复用、CDN 五个方向怎么下手优化,并给出一套可以参考的量化目标。读完你能独立测出一台服务器慢在哪一段,而不是凭感觉猜。
一、TTFB 是什么,由哪几段组成
TTFB 指从发起请求到客户端收到响应第一个字节所花的时间,单位是毫秒。它不是一个单独的动作,而是好几段耗时的叠加。把一个完整请求拆开看,大致是:
| 阶段 | 说明 | 典型耗时(经验值) |
|---|---|---|
| DNS 解析 | 域名解析成 IP | 10-100 ms,有缓存接近 0 |
| TCP 连接 | 三次握手建立连接 | 同机房 1-5 ms,跨地域 30-80 ms |
| TLS 握手 | HTTPS 加密协商 | 1-2 次往返,20-100 ms |
| 服务器处理 | 后端程序执行、查库、渲染 | 波动最大,从十几毫秒到几秒 |
| 首字节传输 | 服务器把第一个字节发出去 | 通常很小,几毫秒内 |
可以看出,真正"服务器慢"主要体现在"服务器处理"这一段,而 DNS、TCP、TLS 属于网络和链路层,更多受机房位置和是否启用连接复用影响。排查时第一步不是优化,而是先判断瓶颈落在哪一段,否则容易优化错方向——花大力气换了更快的 CPU,结果发现慢的是 DNS。
二、用 curl -w 做时间分解检测
最实用的检测手段是 curl -w,它能把上面几个阶段分别打印出来。这套方法在任何一台装了 curl 的机器上都能跑。
# 一次性输出关键时间指标(注意 -o /dev/null 丢弃响应体,-s 静默)
curl -o /dev/null -s -w "\
dns: %{time_namelookup}s\n\
connect: %{time_connect}s\n\
tls: %{time_appconnect}s\n\
ttfb: %{time_starttransfer}s\n\
total: %{time_total}s\n\
http: %{http_code}\n" https://example.com/
对这几个字段的理解是关键:
time_namelookup:从开始到 DNS 解析完成。time_connect:到 TCP 连接建立完成,减去前者就是纯 TCP 耗时。time_appconnect:到 TLS 握手完成,HTTPS 才有,HTTP 会和前一项相同。time_starttransfer:到收到第一个字节,这一项基本等同于 TTFB。time_total:整个请求完成,含下载响应体。
建议多跑几次,跑 10 次取中位数,避免单次网络抖动误导:
# 连续测 10 次,只看 TTFB,接着用 sort 找中位数
for i in $(seq 1 10); do
curl -o /dev/null -s -w "%{time_starttransfer}\n" https://example.com/
done | sort -n | awk '{a[NR]=$1} END{print "中位TTFB:", a[int(NR/2)+1] "s"}'
如果 time_starttransfer 明显大于 time_appconnect,说明慢在后端处理;如果两者接近,说明后端很快,问题在网络链路。这一步能直接帮你分流方向。
除了 curl,还有两个顺手的工具。浏览器开发者工具的 Network 面板里,点开任意请求看 Timing 标签,会看到 Waiting (TTFB) 一栏,这是用户视角的真实体验,适合对照 curl 的结果。服务器侧则用监控工具看后端处理耗时,比如 Nginx 日志里加上 $request_time 和 $upstream_response_time 两个变量,$upstream_response_time 就是后端真正花的时间。
三、优化方向一:降低后端处理耗时
后端处理是 TTFB 波动最大的一块,也是优化收益最高的地方。常见做法:
- 减少同步阻塞操作。后端渲染时逐个串行查库、调用外部接口,会线性累加耗时;能并行的并行,能削峰的削峰。
- 引入缓存。同样的请求结果不必每次重新计算,命中缓存能把处理时间从几百毫秒压到几毫秒,详见第六节。
- 排查慢函数。用 profiling 工具(PHP 的 Xdebug、Python 的 cProfile、Java 的 Arthas)找出耗时最长的函数,往往集中在一两个瓶颈上。
# Nginx 记录后端处理时间,方便定位是不是后端慢
log_format timed '$remote_addr "$request" $status '
'req=$request_time up=$upstream_response_time';
access_log /var/log/nginx/access.log timed;
$request_time 是 Nginx 从收到请求到发出响应的总时间,$upstream_response_time 是转发给后端的等待时间。如果 req 很大而 up 很小,说明慢在 Nginx 自身或网络;如果 up 很大,就是后端应用慢。
四、优化方向二:数据库查询优化
很多动态网站的 TTFB 高,根因在数据库。一个没走索引的查询,扫几十万行,几百毫秒就出去了。排查和优化:
# MySQL 开启慢查询日志(配置文件 /etc/my.cnf 或 /etc/mysql/my.cnf)
# RHEL/CentOS/Rocky 与 Debian/Ubuntu 的配置文件位置可能不同,先确认
mysql -e "SHOW VARIABLES LIKE 'slow_query_log%';"
mysql -e "SHOW VARIABLES LIKE 'long_query_time';"
# 临时开启,记录超过 1 秒的查询(重启失效,长期请写进配置文件)
mysql -e "SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=1;"
拿到慢查询后用 EXPLAIN 分析执行计划,重点看 type 列是不是 ALL(全表扫描)和 rows 扫描行数是否过大。补索引、改写 SQL、给热点数据加缓存,是三条主线。
# 分析一条查询,关注 type / key / rows
mysql -e "EXPLAIN SELECT * FROM orders WHERE user_id=123 AND status=1;"
索引不是越多越好,每个索引都会拖慢写入。一般给高频查询条件、排序字段、关联字段建索引,并定期用 SHOW INDEX 和 information_schema 检查冗余索引。
五、优化方向三:连接复用与网络层
网络层优化不需要改业务代码,收益也稳定。
开启 HTTP 长连接,避免每个请求都重新握手。Nginx 侧:
# http 段
keepalive_timeout 65;
keepalive_requests 1000;
# 反向代理到后端时保持长连接,减少后端建连开销
upstream backend {
server 127.0.0.1:9000;
keepalive 32;
}
给后端启用 HTTP/2,浏览器可以复用一条连接并发多路请求,减少握手次数。HTTPS 场景下开启会话复用(ssl_session_cache)能省下重复的 TLS 握手。
server {
listen 443 ssl http2;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
}
六、优化方向四:页面缓存
缓存是压 TTFB 最直接的手段。命中缓存时后端几乎不参与,TTFB 常常能降到个位数毫秒。Nginx 层面的 proxy_cache(反向代理)和 fastcgi_cache(PHP-FPM 等)都能做。
# http 段定义缓存区
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:100m
max_size=2g inactive=60m use_temp_path=off;
server {
location / {
proxy_cache mycache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_key "$scheme$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
}
X-Cache-Status 返回 HIT 说明命中,MISS 是回源。注意:登录态、购物车这类个性化页面不能整页缓存,否则会把 A 用户的内容发给 B 用户,属于安全事故。这类页面只能做片段缓存或交给对象缓存(Redis/Memcached)处理。
七、优化方向五:CDN 与静态资源分离
把静态资源(图片、CSS、JS、字体)放到 CDN,用户从就近节点取,不消耗源站资源,也不影响源站的 TTFB。对跨地域访问的站点,CDN 能显著降低 DNS、TCP、TLS 这几段耗时。主流云厂商都提供 CDN 和动态加速产品,具体计费和节点数各不相同,选型时以实际测试为准,不要只看宣传数字。
源站和 CDN 之间走回源长连接、开启 gzip/brotli 压缩,也能减少传输字节数和回源次数。
八、量化目标与判断标准
优化要有目标,否则容易陷入"感觉快了"。下面这些是经验参考值,不是硬标准,可根据业务调整:
| 指标 | 体验 | 参考目标 |
|---|---|---|
| TTFB | 优秀 | < 200 ms |
| TTFB | 良好 | 200-500 ms |
| TTFB | 需优化 | 500 ms - 1 s |
| TTFB | 明显卡顿 | > 1 s |
| 后端处理(upstream) | 良好 | < 100 ms |
| 数据库单查询 | 良好 | < 20 ms |
判断时先分清是"每次都慢"还是"偶尔慢"。每次慢通常是后端或数据库设计问题;偶尔慢要查是否有慢查询、缓存穿透、依赖服务抖动或机器资源被打满。用监控看 TTFB 的 P95、P99 分位,比只看平均值更能反映真实体验。
常见问题
Q:TTFB 是 0 或者特别小,正常吗?
可能是命中 CDN 边缘节点或浏览器本地缓存,也可能测的是 127.0.0.1 本机。本机测试只能排除网络因素,判断真实用户 TTFB 要在外部网络环境测。
Q:curl 和浏览器测出来的 TTFB 差很多?
浏览器会带 Cookie、启用压缩、复用连接,和 curl 默认请求不完全一样。对照测试时给 curl 也加上相同请求头(-H "Cookie: ..."、-H "Accept-Encoding: gzip"),并保持相同网络环境。
Q:开了缓存 TTFB 还是高?
先看 X-Cache-Status 是不是一直 MISS。常见原因是缓存键里带了随机参数、Cookie 或时间戳,导致同一页面被视为不同请求,缓存永远不命中。
Q:为什么白天慢晚上快?
典型是并发上来后后端资源争抢加剧:CPU 排队、数据库连接耗尽、缓存被挤。要看高峰期监控,而不是拿半夜的数据下结论。
Q:换了更快的服务器,TTFB 没明显改善?
说明瓶颈可能不在 CPU,而在数据库、磁盘 IO 或外部依赖。先按第二节做时间分解,定位到具体阶段再动手。
总结
TTFB 是首字节时间,由 DNS、TCP、TLS、服务器处理、首字节传输五段组成,其中服务器处理波动最大,是优化重点。
检测第一步用 curl -w 做时间分解,对比 time_appconnect 和 time_starttransfer 就能判断慢在网络还是后端;多测几次取中位数更可靠。
后端处理用缓存和 profiling 优化,数据库用慢查询日志和 EXPLAIN 找全表扫描,网络层靠长连接、HTTP/2、TLS 会话复用,静态资源交给 CDN。
缓存是压 TTFB 最有效的手段,但个性化页面不能整页缓存,否则会串号,属于安全风险。
参考目标:TTFB 优于 200 ms 算优秀,500 ms 以上就该查;监控要看 P95/P99,先分清"每次都慢"还是"偶尔慢"再对症下药。
企业QQ咨询




