QPS容量买少了拦不住,买多了白花钱。本文讲清峰值口径怎么统一、日志和压测怎么算出真实水位、规格里的 QPS 与带宽怎么区分,以及余量和告警阈值该留多少。
Q:为什么「能扛多少 QPS」这句话不能直接信?
- 同一个数字,口径能差十倍。跑静态小文件、后端全命中缓存、连接全复用,和跑一个要查三次数据库的 PHP 页面,是完全两回事。
- 厂商标称通常按 7 层 HTTP 短请求 + 零后端依赖 测出来,你的真实业务能到标称的三成就算不错。
- 更关键的是:QPS 高不高不重要,重要的是哪个接口被打了。首页 2 万 QPS 可能没事,登录接口 300 QPS 就能把数据库连接池打满。
- 所以容量要按「接口」算,不是按「站点」算。一个站点里最贵的那三个接口,决定了你的真实容量。
Q:峰值 QPS 的三种口径差多少?
- 1 秒粒度:最接近瞬时压力,但抖动大,一个爬虫突发就能刷出尖峰,做容量基线容易高估。
- 5 秒或 1 分钟粒度:平滑后更接近后端真实负载,适合定扩容和告警。
- 移动平均:适合看趋势,不适合定规格。
- 实战做法是双口径并行:1 秒峰值定防护规格(设备要能扛瞬时),1 分钟均值定告警阈值(避免半夜乱响)。
- 谈合同前必须和厂商对齐一句:你们的 QPS 是 1 秒峰值还是 5 分钟均值?这一句不问清,后面每个数字都会吵一遍。
Q:怎么从 Nginx 日志算出自己站的真实 QPS 分布?
- 不用装任何监控,访问日志里就有答案。前提是日志格式带
$time_local或$msec。
# 每秒请求数 TOP 20($4 形如 [07/Nov/2026:05:00:01)
awk '{print $4}' /var/log/nginx/access.log | cut -c2-21 \
| uniq -c | sort -rn | head -20
# 只要数字,算 99 分位
awk '{print $4}' /var/log/nginx/access.log | cut -c2-21 \
| uniq -c | awk '{print $1}' | sort -n \
| awk '{a[NR]=$1} END{print "p50="a[int(NR*0.5)], "p99="a[int(NR*0.99)], "max="a[NR]}'
- 按接口拆开看,才知道钱该花在哪:
# 每秒请求数最高的 URI(去掉参数)
awk '{split($7,a,"?"); print $4" "a[1]}' /var/log/nginx/access.log \
| cut -c2-21,22- | sort | uniq -c | sort -rn | head -20
- 三个数记下来就够用:全天最大 1 秒 QPS、近 14 天同时段均值、TOP 3 接口各自的占比。
- 平均值会骗人。一天 8 万 PV 的站,均值不到 1 QPS,但晚八点的 1 秒峰值可能是 200。
Q:自己的安全水位怎么测出来?
- 别在生产环境压。用同配置的预发环境,或者低峰期在源站压,压完立刻回滚配置。
# 逐步加压:并发从 50 到 500,看 QPS 什么时候不再涨
wrk -t4 -c50 -d60s --latency https://example.com/list
wrk -t4 -c200 -d60s --latency https://example.com/list
# 同时盯后端,别只看压测端
# nginx: stub_status 的 active / reading / writing
# php-fpm: php-fpm -tt 看 max_children 是否打满
# mysql: show processlist 看 Sleep 与 Query 堆积
- 拐点的判据不是 QPS 掉,而是QPS 不再涨、P95 延迟翻倍、5xx 开始出现。这个点就是安全水位。
- 算一下理论值对照:并发连接 ≈ QPS × 平均响应时间。200 并发、平均 200ms,理论上限约 1000 QPS。压出来远低于这个数,说明瓶颈在后端不在入口。
- 压测结果只对当次配置有效。加了索引、开了缓存、改了 PHP-FPM 进程数,水位就变了,要重测。
Q:七层的 QPS 和四层的带宽、并发是一回事吗?
- 不是,而且经常被混着卖。四层设备卖的是 带宽 Gbps + PPS + 并发连接数 + 新建连接数,七层设备才卖 QPS。
- 七层设备真正吃紧的是 新建连接速率(CPS) 与 SSL 握手能力,不是单纯 QPS。短连接攻击能把 CPS 打满,QPS 看着不高。
- CDN 和高防的报价单上常见三种配额:带宽、QPS、请求数。三者独立计费,任何一项超了都可能限速或加钱。
- 问清四件事:QPS 是整站还是单域名?超配是限速还是额外计费?回源 QPS 算不算在内?静态资源请求计不计入?
Q:缓存命中率会怎样改变容量结论?
- 影响巨大。边缘 20 万 QPS、命中率 95%,源站只需承接 1 万 QPS 回源,再加一层本地缓存,实际落到 PHP 的可能只有两千。
- 所以容量要分段算:边缘容量、回源容量、应用容量、数据库容量,四段里最窄的那一段就是真实上限。
- CC 攻击恰恰专打最后两段。攻击者刷动态接口、带随机参数让缓存永不命中,边缘看着很闲,源站已经排队了。
- 结论:缓存是容量的一部分,但攻击会把它抽走。做容量规划时至少要按命中率减半来估一次最坏情况。
Q:买的时候该留多少余量?
- 基线取近 14 天同时段峰值的 1.5 到 2 倍,大促或推广期单独按活动预估峰值再乘 1.5。
- 再往上留 30% 的突发额度。攻击的第一分钟永远是尖峰,没有余量就是直接被穿透。
- 不要按攻击峰值买。攻击可以无限大,按攻击峰值买是无底洞;防护买的是「撑住常用业务 + 争取处置时间」。
- 钱不够时的优先级:保住登录、下单、支付三个接口,其余接口降级或排队。这套取舍比多买 20% 规格更管用。
Q:限流阈值和买的规格怎么配合?
- 原则只有一句:限流阈值必须低于设备规格上限。规格 5000 QPS,总闸就设 3000,让限流先动手,别让设备先撞墙。
- 总闸用固定键而不是 IP 键,这样全站总量才控得住:
# 全站总闸:key 固定,所有请求进同一个桶
limit_req_zone "global" zone=total:10m rate=800r/s;
# 贵接口单独一个桶,按 IP 分
limit_req_zone $binary_remote_addr zone=login:10m rate=3r/s;
server {
location / {
limit_req zone=total burst=200 nodelay;
limit_req_status 429;
}
location = /api/login {
limit_req zone=login burst=5 nodelay;
limit_req_status 429;
}
}
- burst 要留一点,否则正常用户连点两下就被拦,投诉比攻击更烦人。
- 返回 429 而不是 444:444 直接断连,埋点和误杀统计全部丢失,事后没法复盘。
- 限流阈值不是买来的,是压出来的。水位测出来 1000,总闸就设 700 到 800。
Q:容量告警阈值怎么设才不吵?
- 双条件触发:QPS 超过基线 2 倍 且 P95 延迟超过基线 3 倍,持续 5 分钟。单看 QPS 会在大促时误报,单看延迟又太晚。
- 再加两条兜底:5xx 比例超过 1%,以及后端连接池使用率超过 80%。
- 告警分级:一级只发群通知,二级打电话。凌晨两点把人叫醒的次数要严格控制,不然第三次就没人接了。
- 扩容有交付时间。云上弹性扩容几分钟,加带宽要工单,加高防规格要商务流程。按最慢的那条链路预留时间,别等打起来了才下单。
Q:还有什么补充建议?
- 把「容量」写成一页纸:每个核心接口的基线 QPS、安全水位、限流阈值、降级开关。出事时照纸执行,不靠回忆。
- 每季度重测一次。业务在长,代码在改,去年的水位今年一定不准。
- 买防护时优先选「可临时升配」的,攻击这两小时能顶上去,比全年买大规格划算得多。
- 别忽略静态资源:图片、下载、视频走独立域名和 CDN,不为它们买应用层 QPS,能省一半预算。