ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

超本地事件下的容量规划:从QPS估算到热点打散实战

超本地事件下的容量规划:从QPS估算到热点打散实战 1. 超本地事件与容量规划到底在解决什么问题1.1 从一次演唱会散场说起如果你负责过一个面向 C 端用户的业务系统大概率经历过这样一种场景一场周末演唱会晚上十点结束三万人在十分钟内同时涌出场馆。有人打开地图打车有人在小程序里找共享单车有人下单夜宵有人发朋友圈。从系统视角看这些请求在时间上高度集中在地理上全部落在同一个商圈、同一个场馆周边。服务器 CPU 在短短几分钟内从 10% 冲到 90%数据库某个分片的连接数被打满部分用户开始看到加载超时。这就是典型的超本地事件hyper local events。它和我们平时说的“晚高峰”“大促”不太一样。晚高峰是全国范围用户分散使用大促是平台级别的持续峰值而超本地事件的特点是流量窗口极短、地理范围极小、请求目标高度集中。对容量规划来说这种事件是对系统弹性能力最严格的考验。1.2 容量规划是什么容量规划capacity planning是指在业务流量到来之前根据预测的负载情况提前评估系统需要多少计算、存储、网络和实例资源并完成相应准备。它回答的核心问题是当流量达到某个规模时系统能不能扛得住需要多少台机器、多少个数据库分片、多少带宽和连接池。传统容量规划更多关注长期增长趋势比如根据过去三个月的日活和请求量增长曲线预估下一个季度需要扩容多少台服务器。这种方式在业务相对平稳时很有效但在超本地事件面前会出现明显不足。原因在于超本地事件的流量不是缓慢爬坡而是瞬间打满不是均匀分散在所有节点上而是集中打在少数几个热点上。如果我们只按日均 QPS 或历史峰值做容量评估很容易漏掉真正会出问题的局部瓶颈。1.3 为什么超本地事件让容量规划变难常规容量规划模型通常会做两个隐性假设一是流量在时间上相对平滑峰值不会在几分钟内突然放大几十倍二是流量在所有节点上分布均匀不会出现某个实例或某个数据分片独自承担绝大多数压力。超本地事件恰恰同时打破这两个假设。我们用一个具体例子来说明。假设一场活动有三万人参与高峰集中在十分钟内。如果每个人在这十分钟里平均发起三次请求那么面向用户接口的基础 QPS 并不高大约只有 90 QPS。这个数字看起来轻轻松松任何一台普通服务器都能扛住。但实际情况往往是这 90 QPS 只是入口流量每次请求会触发多次下游调用同时因为所有用户都点击同一个商圈的服务、查看同一批商户、争夺同一批运力资源数据库里某个热点 Key 的分片会被打到极高压力。换句话说容量规划不能只看入口总流量还要看链路放大情况和热点分布情况这正是超本地事件最容易被低估的地方。2. 超本地事件的流量特征分析要做容量规划第一步不是算数字而是先理解这类事件的流量特征。只有把特征梳理清楚后面建的估算模型才有依据。2.1 时间聚集性峰值窗口极短超本地事件最显著的特征是时间聚集。无论是演唱会散场、球赛结束、末班地铁到站还是某个网红店开业、某个商圈集中发券用户的动作几乎是在同一时间段内触发的。我用“分钟级突发”这个词来概括。比如演唱会散场人流从场馆涌出的时间窗口通常在 15 到 30 分钟而用户集中发起打车、支付、查询请求的时间可能只有前 10 分钟。这意味着系统从低水位到峰值可能只需要几分钟根本没有给自动伸缩留出从容的反应时间。Kubernetes 的 HPA 默认的扩容周期是几十秒到分钟级加上 Pod 启动、注册、拉取流量真正生效可能需要 3 到 5 分钟。对于一场十分钟就结束的流量高峰等自动扩容完成峰值可能已经过去了。2.2 空间聚集性流量集中在少数热点超本地事件的第二个特征是空间聚集。所有流量都指向同一个地理区域、同一个业务对象。比如一场在五棵松体育馆举办的演唱会后地图服务的所有路径规划请求几乎都集中在“五棵松”这个 POI 附近打车平台的运力池也集中在该区域附近的餐厅、便利店面临瞬间涌入的下单请求。空间聚集对系统最直接的影响是数据热点。如果业务按城市 ID 或区域 ID 做数据分片那么超本地事件会导致某一个分片的流量远高于其他分片。即使整个集群的资源还有大量富余热点分片对应的数据库、缓存、连接池也可能已经被打满。这是超本地事件容量规划和常规容量规划最大的不同常规规划关注集群总量超本地事件还要额外关注热点数据单点的承载能力。2.3 可预测与不确定并存超本地事件有一个有利条件它在很大程度上是可预测的。我们知道活动的时间、地点、预计人数可以提前很久做准备。这和“突发热点新闻”导致的服务暴涨完全不同后者的不可预测性更高。但可预测不等于完全可控。活动当天实际到场人数、天气变化、散场时间是否延迟、用户是否集中使用某一功能这些细节存在不确定性。因此超本地事件的容量规划不能只做一套静态预估而是要基于预测结果做多档预案并在活动前通过压测验证。可预测的部分让我们能够提前准备不确定的部分要求我们留有冗余和降级方案。为了更直观地理解超本地事件和传统业务的差异我们对比一下对比维度传统日常业务超本地事件流量流量曲线平滑有规律分钟级陡增峰值窗口短地理分布全国/全城分散集中在某个商圈、场馆数据访问分布在各分片热点 Key 集中访问可预测性依赖历史趋势已知活动时间地点可提前准备主要风险整体资源不足局部热点被打满、雪崩3. 容量规划的基本指标体系聊完流量特征我们进入容量规划的技术细节。在做任何评估之前需要先有一套统一的度量指标。没有指标讨论容量就没有依据。3.1 流量指标QPS、TPS 与并发连接数QPSQueries Per Second是每秒查询数通常用来描述接口或数据库承受的请求压力。TPSTransactions Per Second是每秒事务数更多用于描述一次完整业务操作的频率比如一笔订单从创建到支付的完整过程。在实际容量评估中这两个概念经常混用但我们需要清楚它们的区别。并发连接数指的是系统同一时刻维持的活跃连接数量。根据 Littles Law并发数约等于 QPS 乘以平均响应时间。举个例子如果一个接口 QPS 是 100平均响应时间是 200 毫秒那么它维持的并发连接大约是 100 × 0.2 20。理解这个关系很重要因为很多背压和连接池配置都与此有关。单看 QPS 会忽略响应时间变差带来的并发量上升进而导致连接池被占满。3.2 资源指标CPU、内存、磁盘与网络容量规划最终要落到资源上。CPU 使用率反映计算密集型处理能力内存使用率反映对象实例和数据缓存占用磁盘 IO 反映读写压力网络带宽反映数据传输量。一个系统可能 QPS 不高但每次请求返回的数据量很大导致网络带宽先成为瓶颈。也可能业务逻辑简单但所有请求都触发大量数据库扫描磁盘 IO 先被耗尽。因此容量规划不能只盯着一个指标。更合理的做法是为每个核心链路确定“瓶颈指标”也就是在资源耗尽时最先达到上限的那一项指标。比如一个纯内存计算的服务瓶颈大概率是 CPU 或内存一个依赖大量 SQL 查询的服务瓶颈大概率在数据库连接数或磁盘 IO一个返回大流量视频流的服务瓶颈大概率是带宽。3.3 容量水位的定义有了指标之后还需要定义水位。水位是一个阈值概念用来描述系统资源使用到什么程度算是“需要关注”。不同团队定义不同我通常采用以下三档安全水位CPU 使用率 40% 到 60%。在这个水位下系统有足够的余量应对流量抖动和实例故障。告警水位CPU 使用率 70% 到 80%。此时系统开始进入高负载状态需要有预案准备比如扩容、降级。危险水位CPU 使用率 90% 以上。此时系统随时可能出现排队、超时、雪崩必须立即介入。需要注意的是水位阈值不是拍脑袋定的而是通过压测和线上监控综合得出的。如果某个服务在 CPU 80% 时响应时间已经明显劣化那么它的告警水位就应该下调如果另一个服务在 CPU 90% 时依然平稳那么它可以适当上调告警水位。4. 超本地事件的容量估算方法理解指标之后我们需要一套可操作的容量估算流程。这里介绍两种方法自上而下的业务估算和自下而上的压测验证。两种方法需要结合使用才能得到比较可靠的结果。4.1 自上而下从业务规模推导流量自上而下的估算思路是从已知的活动规模出发根据用户行为假设推导出系统需要承受的 QPS 和资源需求。流程大致如下确定参与人数根据售票数、报名人数或历史同类活动的到场率估算。估算同时在线率不是所有参与者都会在峰值窗口同时使用服务根据活动类型预估一个比例。估算人均请求数用户在这段时间内平均会触发多少次核心接口请求。确定峰值时间窗流量集中的时间段通常取 5 到 15 分钟。计算基础 QPS总请求数除以峰值秒数。乘以下游放大系数每一次用户请求会触发多少次下游服务调用得到系统整体需要承受的 QPS。这种方法的好处是快不需要任何系统数据就能在会议桌上做出初步估算。但它依赖很多假设比如在线率、人均请求数、下游放大系数这些假设如果有偏差估算结果也会偏差很大。所以自上而下估算只能作为出发点不能作为最终答案。4.2 自下而上用压测确认单机能力自下而上的核心是压测。我们对核心服务做压力测试测出单个实例在满足响应时间要求的前提下最多能支撑多少 QPS、多少并发、多少带宽。这个数据就是容量规划中最重要的单点基数。压测时要注意两点一是压测场景要贴近真实业务比如包含完整的下游调用链路、数据库访问和缓存访问而不是只压一个空接口二是要设置明确的响应时间 SLA比如 P99 小于 500 毫秒超过这个阈值即使系统还没崩溃也算容量不足。真实业务中一个接口可能在 QPS 500 时仍然能正确返回结果但响应时间已经从 100 毫秒劣化到 2 秒用户体感已经不可接受。因此压测的判定标准必须是“满足 SLA 的前提下的最大 QPS”。4.3 实例演唱会场景容量估算我们用一个具体的演唱会场景把整个估算过程串起来。假设一场演唱会在某个体育馆举办预计参与人数 3 万人。用户集中行为发生在散场后 10 分钟内包括打开 App、查看地图、呼叫车辆、完成支付。估算参数如下参数数值说明参与人数30000场馆容纳人数同时在线率60%散场后打开 App 的用户比例人均请求数3每个用户查看、呼叫、确认的平均请求次数峰值时间窗10 分钟流量最集中的时间段下游放大系数4每个入口请求触发约 4 次下游调用单机压测 QPS500符合响应时间 SLA 的最大 QPS冗余倍数2为故障和流量抖动预留一倍余量基础入口 QPS 计算如下总请求数 30000 × 0.6 × 3 54000峰值秒数 10 × 60 600基础 QPS 54000 ÷ 600 90考虑下游放大后系统整体需要承受的总 QPS 90 × 4 360单实例安全 QPS 500 ÷ 2 250所需实例数 360 ÷ 250 ≈ 2 个从总量上看两个实例似乎就足够了。但这个结论非常危险因为我们还没有考虑热点。假设高峰期间 30% 的流量集中在同一个商圈热点数据上那么热点分片需要承受的数据库 QPS 90 × 0.3 × 4 ≈ 108。如果数据库单分片的支撑上限只有 100 QPS那么这个热点分片在活动当天必然会成为瓶颈。这就是为什么容量规划必须同时算“总量”和“热点单点”。4.4 关键点热点单点容量超本地事件容量规划和传统容量规划最大的区别就是这个热点单点问题。很多时候集群总资源是够的瓶颈在于某一条数据被集中访问。以打车平台为例运力池通常会按城市或区域进行分片。平时城市内各区域流量分散每个分片压力相近但演唱会结束后所有请求都集中在这个商圈的运力池 Key 上。如果这个 Key 只存在于一个分片中那么无论整个集群有多少个分片压力都会被这个单分片承接。解决热点单点问题的思路主要有几种一是设计更细粒度的分片键比如把商圈运力池进一步拆分为多个子区域避免所有请求集中在同一个 Key 上二是在数据库前面加多级缓存把高频请求拦截在缓存层三是对热点 Key 做读写分离把读流量分散到只读副本。5. 实战案例Python 容量估算小工具理论讲完我们动手写一个简单的容量估算工具。这个工具用 Python 实现输入活动基本参数输出建议实例数和热点分片压力方便在每次活动前快速做一轮初步评估。5.1 需求与设计工具需要实现以下功能根据参与人数、在线率、人均请求数、峰值时间窗计算基础 QPS根据下游放大系数计算系统总 QPS根据单机压测 QPS 和冗余倍数计算建议实例数根据热点流量占比计算热点分片需要承受的 QPS。我们用 dataclass 定义输入参数用独立函数完成每一段计算逻辑最后在 main 函数中汇总输出。这样代码结构清晰后续如果要接入配置文件或命令行参数扩展也很方便。5.2 完整代码# 文件路径capacity_estimator.py import math from dataclasses import dataclass dataclass class CapacityInput: total_users: int # 参与活动的总人数 online_rate: float # 高峰时段同时在线比例 requests_per_user: int # 人均请求数 peak_minutes: float # 峰值持续时间分钟 concurrency_factor: float # 下游调用放大系数 single_node_qps: float # 压测得到的单机支持 QPS reserve_factor: float # 冗余倍数 hotspot_share: float # 热点流量占总流量比例 def calc_base_qps(data: CapacityInput) - float: 计算基础入口 QPS不考虑下游调用放大。 total_requests data.total_users * data.online_rate * data.requests_per_user peak_seconds data.peak_minutes * 60 return total_requests / peak_seconds def calc_total_qps(base_qps: float, data: CapacityInput) - float: 计算包含下游放大后的系统总 QPS。 return base_qps * data.concurrency_factor def calc_instance_count(total_qps: float, data: CapacityInput) - int: 根据单机能力和冗余倍数计算建议实例数。 safe_qps_per_node data.single_node_qps / data.reserve_factor return math.ceil(total_qps / safe_qps_per_node) def calc_hotspot_qps(base_qps: float, data: CapacityInput) - float: 计算热点分片需要承受的 QPS含下游放大。 return base_qps * data.hotspot_share * data.concurrency_factor def main(): data CapacityInput( total_users30000, online_rate0.6, requests_per_user3, peak_minutes10, concurrency_factor4.0, single_node_qps500, reserve_factor2.0, hotspot_share0.3, ) base_qps calc_base_qps(data) total_qps calc_total_qps(base_qps, data) instance_count calc_instance_count(total_qps, data) hotspot_qps calc_hotspot_qps(base_qps, data) print(f基础接口峰值 QPS{base_qps:.1f}) print(f含下游放大总 QPS{total_qps:.1f}) print(f建议实例数{instance_count}) print(f热点分片 QPS{hotspot_qps:.1f}) if __name__ __main__: main()5.3 运行与结果说明在命令行中执行python capacity_estimator.py预期输出如下基础接口峰值 QPS90.0 含下游放大总 QPS360.0 建议实例数2 热点分片 QPS108.0我们来解释一下这些结果的业务含义。基础接口峰值 QPS 是 90说明入口请求压力并不大。含下游放大后的总 QPS 是 360这才是整个系统需要真实面对的压力。建议实例数是 2意味着从集群总量看两台满足压测标准的实例就够了。但热点分片 QPS 是 108这个数字需要和数据库分片的真实能力做对比。如果数据库单分片能力低于这个值就需要做热点打散或增加分片。我在实际使用中通常不会把“建议实例数”直接当成最终答案。工具的定位是快速筛选当估算结果明显超出当前集群容量时我们就需要进入详细的压测和架构评审环节。如果估算结果显示容量充足也不能掉以轻心还需要通过演练验证热点单点的真实表现。6. 支撑容量规划的架构手段容量规划不只是评估数字更重要的是把评估结果落地到系统架构中。下面这些手段是支撑超本地事件容量规划的关键。6.1 预扩容优先于自动扩容面对超本地事件我的建议是预扩容优先自动扩容兜底。因为超本地事件的流量是分钟级突发的自动扩容从检测到生效往往赶不上峰值。具体做法是在活动开始前一到两个小时就把核心服务扩容到目标实例数。这个目标实例数来源于容量估算和压测结果。比如估算需要 10 个实例可以预先扩容到 12 个留出一定余量。活动期间保持自动扩容开启作为意外流量上升的兜底手段。活动结束后再逐步缩容控制成本。如果是 Kubernetes 环境可以通过修改 deployment 的副本数实现预扩容也可以在活动前用 CronJob 自动调整 HPA 的最小副本数。无论哪种方式核心原则都是一样的把流量高峰需要的大多数资源提前准备好而不是寄希望于运行时伸缩。6.2 热点 Key 打散热点打散是处理空间聚集问题的核心手段。以缓存为例一个热点 Key 在超本地事件中可能被大量请求同时读取单一缓存分片会成为瓶颈。常用的打散策略是给热点 Key 加随机后缀。比如原始 Key 是shipping_pool_beijing_chaoyang我们可以拆分为shipping_pool_beijing_chaoyang_01、shipping_pool_beijing_chaoyang_02等多个 Key分散到不同的缓存分片。读请求随机访问其中一个带后缀的 Key写请求则需要同时更新所有子 Key因此要谨慎评估写放大对一致性的影响。更彻底的做法是重新设计分片键。如果业务允许可以把一个城市的大区域拆分成多个小区域每个小区域有独立的资源池和分片键这样流量天然分散到多个分片上。分片键设计需要结合业务读写模型不能一概而论但方向是一致的让任何单个 Key 都不至于承载超出单点能力的流量。6.3 限流、降级与熔断无论容量规划做得多充分总有可能出现超出预估的流量。这时候保护系统不被压垮的机制就是限流、降级和熔断。限流是在入口处控制请求速率超过阈值的请求直接返回失败或排队保证系统其余部分不被拖垮。降级是在依赖服务压力过大时关闭非核心功能比如关闭个性化推荐、取消实时推送把资源留给核心链路。熔断是在下游服务连续错误率达到阈值时暂时断开对该服务的调用让下游恢复后再放流量进来。这三者配合使用构成了系统的最后一道防线。容量规划解决的是“正常情况下足够用”的问题限流降级解决的是“异常情况下不死掉”的问题。两者缺一不可。6.4 压测与预案演练容量评估的准确性最终要靠压测来验证。活动前至少要做一轮全链路压测模拟超本地事件下的流量模型包括突增流量、热点集中访问、慢依赖放大等场景。压测结果要回填到容量估算模型中校准参数。预案演练同样重要。活动前需要把限流阈值、降级开关、扩容脚本、应急预案完整走一遍确保每个运维同学和开发同学都知道在什么条件下执行什么操作。一个容易被忽视的细节是降级开关本身也可能成为瓶颈。如果降级开关依赖一个外部配置中心而配置中心在高压下不可用那么降级操作就执行不了。所以核心降级开关最好有多条触发路径比如人工直接修改配置数据库、调用运维平台接口等。7. 常见问题与排查思路即便做了充分的容量规划和架构准备超本地事件中仍然可能出现各种问题。下面是几个高频问题与排查思路。7.1 热点分片倾斜现象整个集群的 CPU 使用率不高但某个数据库分片或 Redis 分片的 CPU 飙高响应时间变长甚至出现连接拒绝。常见原因热点 Key 全部集中在一个分片分片键设计不合理或者缓存未命中导致请求穿透到数据库。排查思路先通过监控找到 CPU 飙高的具体分片和对应的慢日志确认是哪个 Key 出了问题然后判断请求是否打到了缓存层未命中的原因是什么最后根据热点 Key 的读写比例决定打散方案。解决方案增加缓存层拦截热点读请求热点 Key 加随机后缀打散或者优化分片键设计。活动结束后要复盘分片键是否需要长期调整。7.2 超卖与库存扣减现象活动期间用户同时抢购同一商品或同一运力资源系统竟然出现超卖扣减库存数量超过实际库存。常见原因在高并发下多个请求同时读到库存数余量并在扣减前没有做原子操作或加锁控制。排查思路检查库存扣减是走数据库行锁还是 Redis Lua 脚本确认原子性是否得到保证同时排查库存预热和缓存一致性的实现方式。解决方案核心扣减操作建议用 Redis Lua 脚本保证原子性或者使用数据库乐观锁、悲观锁实现。任何扣减方案的改动都要经过压测和事务正确性验证不能直接在生产环境改。7.3 雪崩现象上游流量变大后某一个服务先出现超时随后大量线程阻塞在该服务的调用上导致上游资源耗尽最终整个链路崩溃。常见原因系统没有限流和熔断机制或者超时设置过长导致下游故障快速传导到上游。排查思路通过链路追踪查看故障源检查超时配置、线程池大小、限流阈值。确认是否已经有熔断器在工作。解决方案设置合理的超时时间引入熔断机制核心链路做好降级预案。容量规划不是要消灭流量高峰而是要保证即使在高峰中部分服务出现故障也不会波及全站。问题现象常见原因解决思路集群总 QPS 不高但某分片 CPU 100%热点 Key 集中访问热点打散、加缓存、优化分片键活动期间出现超卖扣减操作无原子性使用 Redis Lua 脚本或数据库锁一个服务超时导致全链路阻塞无熔断、超时时间过长配置合理超时、引入熔断降级自动扩容跟不上流量突增HPA 生效延迟活动前预扩容自动扩容兜底应急预案无法执行降级开关依赖不可用组件准备多条降级触发路径8. 最佳实践与工程建议到这里容量规划的方法、工具和常见问题都讲完了。最后我想分享一些在真实工程中积累的实践建议。8.1 把容量规划做成例行机制不要只在大型活动前才做容量规划。更推荐的做法是建立例行机制每周或每两周对核心服务的容量水位做一次评审结合监控数据判断是否需要扩容每次有版本上线或业务功能变更时评估是否会影响容量模型。这样做的好处是容量管理不再是一次性的“救火”动作而是持续迭代的过程。每次活动结束后把实际监控数据和预估数据进行对比校准参数下一次估算就会更准。8.2 关注 P99 而不是平均值容量评估中平均响应时间容易掩盖问题。假设一个接口平均响应时间是 200 毫秒但 P99 是 2 秒说明有 1% 的请求体验已经很差。在超本地事件中那 1% 的请求往往是流量最高峰时产生的代表了真实体验的底限。所以压测和监控都应该以 P99 甚至 P999 为重要指标。扩容和限流阈值的设定也要以分位数响应时间不劣化为前提。8.3 数据驱动地校准参数容量估算模型中的每一个参数都应该有依据并且定期校准。比如在线率、人均请求数来自线上埋点和历史活动数据单机 QPS 来自压测报告下游放大系数来自链路追踪系统。没有数据支撑的参数在评审中要被打回重新评估。我建议为每个核心链路维护一张容量参数表记录参数值、数据来源、更新时间。这样无论是谁接手都能快速理解当前容量评估的假设基础。8.4 在成本和风险之间找平衡容量规划不只是技术问题也是成本问题。扩容足够多的实例一定更安全但预算可能不允许。具体项目中可以通过分级保障来控制成本核心链路按满足峰值的 2 倍冗余规划非核心链路接受适当降级。给出容量评估结论时不要只给一个“建议实例数”。我会在结论里附带三个数据需要提前扩容到多少台实例、预计峰值 QPS 会打到多高、触发应急预案的报警条件是什么。这三个数据分别对应资源准备、压测目标和运行时决策比单独一个数字有用得多。把这些数据写清楚运维同学、研发同学和业务方对活动的容量预期就完全一致了活动当天的处理也会从容很多。
返回列表