电商系统开发中,最容易被误判的一类故障是“高峰期服务器卡了”。我曾参与过一次促销活动排障:接口 P99 从 420 毫秒升到 8.6 秒,应用服务器 CPU 只有 58%,内存也没有明显上涨,扩容两次几乎没有改善。最后真正拖慢下单链路的,不是机器算力,而是库存事务锁等待、数据库连接池排队,以及营销查询和库存扣减共用资源造成的级联阻塞。

这类问题说明,电商系统的性能治理不能停留在“加机器、加缓存、上微服务”三个动作上。高峰期卡顿本质上是请求链路中某个环节的排队、争抢、超时或重试被放大。本文将从用户请求进入系统开始,沿着网关、应用服务、缓存、数据库、消息队列和第三方依赖逐层定位,并进一步说明开发团队如何用指标、责任边界和复盘机制,把一次偶发卡顿变成可复用的工程知识。
很多团队第一次遇到高峰期卡顿时,都会先看 CPU、内存和磁盘利用率。如果 CPU 没有达到 80% 以上,就认为服务器还有余量;如果内存没有持续上涨,就认为应用运行正常。这种判断只适合资源型瓶颈,不适合排查排队型瓶颈。
线程池、数据库连接池、Redis 连接、消息消费者和外部接口,都可能在资源尚未完全耗尽时形成等待。一个请求可能只占用很少的 CPU,却长时间等待数据库连接;一个应用节点可能 CPU 很低,却因为线程都阻塞在下游接口上而无法继续处理新请求。
我判断高峰期问题时,第一原则不是“哪个资源最高”,而是“哪个环节的等待时间最长”。等待时间通常比资源利用率更接近用户感知。应用线程等待数据库连接、数据库事务等待锁、消息消费者等待第三方接口,这些等待都会表现为接口变慢。
因此,排查时至少要同时观察以下几组指标:
只查看 CPU 和内存,实际上只看到了系统的“体温”,却没有看到请求在哪里排队。

平均值是最容易被管理层接受、也最容易误导排障的指标。假设 1000 次请求中有 980 次在 300 毫秒内完成,20 次请求耗时 20 秒,平均响应时间可能仍然只有 694 毫秒。对监控看板来说,这个数字不算特别糟,但那 20 个请求对应的用户可能正好是提交订单、支付或抢购库存的用户。
电商系统尤其需要关注长尾延迟。商品浏览接口可以容忍少量慢请求,但库存扣减、订单创建和支付状态查询的超时,往往会引起用户重复点击、重复提交和客服投诉。更严重的是,重复请求还会进一步增加系统压力。
我通常会把接口指标分成三层看:
如果一个下单接口平均耗时 350 毫秒,P99 却达到 6 秒,我不会接受“平均值还不错”这个结论。下一步必须把这 1% 的慢请求按接口参数、商品 ID、用户类型、数据库节点、应用节点和请求链路拆开。
电商系统的请求并不是彼此独立的。商品详情页可能查询库存和营销规则;购物车可能同时校验价格、优惠券和配送范围;下单接口可能调用库存、订单、营销、会员和支付服务。只要其中一个依赖变慢,同步调用就会把等待时间传给上游。
当上游线程因为等待而不释放时,线程池会逐渐排满。新请求进入后继续排队,最终网关出现超时。此时监控上可能看到多个服务同时变慢,但真正的根因只有一个:某个底层依赖先发生了拥堵。
系统性卡顿常常不是所有模块一起坏了,而是一个低容量环节被多个上游共享,最终造成全链路排队。
许多团队会说:“平时接口只有 200 毫秒,压测也通过了,为什么大促还是卡?”问题通常出在压测模型不完整。压测只打了商品查询,没有模拟优惠券校验、库存扣减、订单写入、消息发送和支付回调,得到的只能是单接口性能,不是完整业务链路性能。
另一个常见问题是压测流量过于平均。真实促销流量往往具有明显的尖峰:活动开始前几分钟,用户同时刷新页面;爆款商品集中访问少数几个 Key;某一时刻大量订单争抢同一库存记录。平均流量可能不高,但瞬时流量、热点集中度和写入冲突非常高。
在我参与过的一次模拟中,整体商品查询 QPS 只有日常峰值的 3.2 倍,但前 20 个热门商品贡献了 68% 的库存查询请求。系统总体流量看起来可控,热点商品所在的缓存 Key 和数据库行却已经进入高竞争状态。
因此,容量评估不能只问“每秒有多少请求”,还要问:

很多人把高峰期等同于“下单高峰”,但系统压力往往提前出现。活动预热时,用户集中刷新详情页;优惠券开放时,领券接口和资格校验接口先受到冲击;活动结束后,订单状态查询、售后咨询和消息补偿又会形成新的压力。
如果团队只为下单接口扩容,却没有保护商品详情、活动规则、购物车和订单查询,用户仍然会感受到系统卡顿。更复杂的是,详情页变慢会导致用户重复刷新,重复刷新进一步放大流量,形成“慢,重试,更慢”的反馈回路。
我建议将一次活动拆成至少四个时段观察:
下面这个案例使用了脱敏后的典型场景,数字用于说明排查方法,不代表某一家企业的公开统计。某电商系统在活动开始后,下单接口平均响应时间从 280 毫秒升到 1.4 秒,P99 从 900 毫秒升到 8.6 秒,超时率达到 4.8%。应用服务器 CPU 仅从 45% 上升到 58%,因此第一轮判断误以为“应用资源还够”。
继续看连接池后,情况发生了变化。订单服务的数据库连接池使用率从 62% 升到 97%,获取连接平均等待时间从 12 毫秒升到 740 毫秒。数据库慢查询数量并没有成倍增加,但库存扣减相关事务的锁等待从平时的几十毫秒上升到 2 秒以上。
进一步追踪发现,营销规则查询和库存扣减使用了同一组数据库连接。营销规则查询本身并不复杂,但部分请求在事务范围内执行了多次查询,导致连接长时间不释放。当热点商品的库存记录被大量并发更新时,订单请求一边等待连接,一边等待库存行锁,最终表现为下单接口整体变慢。
这个案例中,直接扩容应用节点帮助有限,因为新增节点仍然要争抢同一个数据库连接池和热点库存行。更有效的处理顺序是:

CPU 低可能意味着应用没有足够的可运行任务,也可能意味着大量线程处于等待状态。线程阻塞在数据库、网络或锁上时,CPU 不会因此升高。更糟糕的是,线程池可能已经全部被慢请求占用,新请求只能在队列中等待。
排查应用层时,我不会只看 CPU,而会同时确认线程状态。若大量线程处于等待数据库连接、等待网络响应或等待锁的状态,应该优先检查依赖服务和超时配置,而不是继续增加应用实例。
数据库变慢至少有四种不同原因:SQL 执行本身慢、锁等待慢、连接获取慢、主从复制延迟慢。它们的修复方法完全不同。
| 表现 | 优先观察 | 可能原因 | 第一动作 |
|---|---|---|---|
| 单条查询执行时间长 | 执行计划、扫描行数 | 索引缺失、条件失效、全表扫描 | 检查 SQL 和索引匹配关系 |
| SQL 本身不慢但请求很慢 | 锁等待、事务持续时间 | 并发更新同一行或事务过长 | 缩短事务、拆分热点写入 |
| 应用拿不到数据库连接 | 连接池等待时间、连接泄漏 | 连接未释放、慢事务占满连接 | 检查连接生命周期和池配置 |
| 读请求结果延迟或不一致 | 主从延迟、路由策略 | 复制滞后、读写分离策略不当 | 梳理一致性要求和读路由 |
“慢查询”只是现象,不是根因。如果没有先区分执行耗时、锁等待和连接等待,直接优化 SQL 可能耗费大量时间,却无法解决真正的卡顿。
缓存命中率是重要指标,但它不能单独证明缓存设计是安全的。全站缓存命中率 95%,并不代表最热门的那几个 Key 没有被击穿;也不代表大量 Key 同时过期时,数据库能够承受瞬时回源。
我会把缓存问题拆成三个维度:命中率、集中度和失效行为。命中率回答“多少请求从缓存拿到了结果”;集中度回答“访问是否集中在少数 Key”;失效行为回答“缓存失效时回源是否受控”。
例如,100 万个商品中只有 10 个爆款,爆款承接了大部分访问。全站命中率看起来很好,但只要其中一个热点 Key 同时过期,大量请求就可能穿透到数据库。此时应该关注单 Key QPS、回源并发和重建锁,而不是只看总体命中率。
拆分服务可以带来独立部署、独立扩容和职责隔离,但也会增加网络调用、链路追踪、配置管理、故障传播和数据一致性成本。一个原本在单体内部完成的函数调用,拆成多个服务后,可能变成多次网络请求和序列化操作。
如果业务链路本身没有清晰边界,过早拆分会把一个复杂问题分散到更多服务中。故障发生时,团队需要在多个日志系统、多个监控面板和多个发布单元之间来回切换,反而降低定位效率。
我更关注服务边界是否对应真实业务职责,而不是服务数量。库存、订单和支付往往需要不同的可靠性策略,但营销规则、商品展示和推荐服务不一定都需要立即拆成独立服务。

用户说“页面卡”,可能是静态资源下载慢、首屏接口慢、接口返回后前端渲染慢,也可能是请求成功但业务状态迟迟没有更新。不同问题需要不同团队参与,不能直接把所有责任交给后端。
我会先通过浏览器网络请求、网关日志和链路追踪确认三个时间点:
如果网关收到请求前已经耗时很长,应检查网络、CDN、WAF 或客户端重试。如果应用处理耗时长,应继续向缓存、数据库和第三方依赖下钻。如果接口很快但页面仍然慢,则应查看资源体积、前端渲染和串行请求。
一个接口总耗时可以拆成排队时间、业务计算时间、数据库时间、缓存时间、外部依赖时间和序列化时间。虽然实际系统的埋点方式不同,但排查思路必须尽量接近这种分解。
总耗时 = 网关排队 + 应用排队 + 业务计算
+ 缓存访问 + 数据库访问
+ 外部依赖 + 响应序列化
如果总耗时增加 2 秒,而数据库只增加 100 毫秒,就不能继续把注意力集中在 SQL 上。可能是应用线程池排队 1.5 秒,也可能是第三方接口超时后触发重试。
| 时间分解结果 | 优先怀疑 | 验证方法 |
|---|---|---|
| 应用排队时间上升 | 线程池不足、任务执行过慢、同步调用过长 | 查看活跃线程、队列长度和线程状态 |
| 数据库获取连接时间上升 | 连接池耗尽、长事务、连接泄漏 | 对比连接使用率和连接生命周期 |
| SQL 执行时间上升 | 执行计划变化、数据量增长、索引失效 | 查看执行计划、扫描量和锁情况 |
| 外部依赖时间上升 | 第三方变慢、超时配置过长、重试风暴 | 查看连接耗时、响应耗时、重试次数 |
| 序列化时间上升 | 响应对象过大、重复字段、压缩异常 | 比较响应体大小和序列化耗时 |
容量问题通常表现为请求量增加后,系统吞吐达到平台期,资源利用率和处理时间同步上升。争抢问题表现为热点数据、连接、线程或锁被多个请求竞争。依赖问题则表现为某个下游服务变慢后,上游同步等待和重试增加。
三类问题的解决方式不能混用。容量不足可以通过扩容、优化资源配置或削峰解决;争抢问题需要降低热点竞争、缩短临界区或隔离资源;依赖问题需要设置超时、熔断、降级和异步化。

多个指标同时异常时,最先发生变化的指标通常比最严重的指标更接近根因。例如,网关超时率在 10:05 上升,数据库连接池在 10:06 达到满载,应用线程拒绝在 10:07 出现,那么应优先检查 10:05 之前的下游响应和流量变化。
我会把时间线至少精确到分钟,并标注发布、配置变更、缓存刷新、活动开始、数据库切换和第三方告警。很多故障并不是流量本身造成的,而是活动开始前刚刚发布的代码改变了查询条件,或者缓存预热策略导致大量 Key 在同一时间失效。
CDN、WAF、负载均衡和网关是用户请求的第一道入口。这里出现问题时,应用服务可能完全没有异常,但用户仍然打不开页面或接口频繁超时。
需要检查的不是单纯的访问量,而是流量是否均匀分配、是否出现异常来源、是否发生错误限流,以及请求是否被错误路由到某个节点。部分节点负载异常而整体平均负载正常,是接入层常见的假象。
线程池决定应用能同时处理多少任务,数据库连接池决定应用能同时占用多少数据库连接。二者并不是越大越好。线程数过多会增加上下文切换,连接数过多会把数据库推向更激烈的锁竞争和内存压力。
如果线程池远大于数据库连接池,线程会大量阻塞在获取连接阶段;如果连接池远大于数据库实际承载能力,更多并发请求会同时进入数据库,反而使锁等待变长。
我会重点关注以下信号:
对核心链路而言,正常请求、重试任务、报表查询和后台批处理不应无条件共享同一组资源。资源隔离往往比单纯增加资源上限更有效。
缓存设计的关键不是“有没有使用缓存”,而是请求模式和失效策略是否匹配业务。商品详情适合缓存,但库存扣减不能简单依赖一个可能过期的缓存值;活动规则可以缓存,但规则变更时必须考虑旧数据残留和集中失效。
常见故障有四种:
治理时要根据故障类型选择方案。不存在数据可通过空值缓存或布隆过滤器减少穿透;热点 Key 可使用互斥重建、逻辑过期或本地缓存;集中失效可通过过期时间随机化和分批刷新降低冲击;热 Key 则需要拆分访问、复制热点数据或限制无效刷新。
电商数据库的压力不是一个维度。商品浏览会产生大量读请求,订单和库存会产生关键写请求,营销规则和后台报表可能产生复杂查询。读压力可以通过缓存、读副本和查询优化缓解,但写压力和锁压力需要重新设计事务与数据模型。
排查数据库时,我会按以下顺序进行:
库存扣减是电商数据库最需要谨慎处理的写场景之一。库存行更新涉及并发一致性,但并不意味着所有业务查询都应该放在同一个长事务中。事务越长,锁持有时间越久,热点商品越容易把下单请求拖入排队。
把订单通知、积分计算、营销统计和物流同步放入消息队列,可以降低同步链路压力,但消息队列只是把处理时间从当前请求转移到了后续阶段。如果消费者能力不足,用户会看到订单状态迟迟不更新,运营人员会看到数据报表延迟,最终仍然形成业务投诉。
需要同时比较生产速率和消费速率。只看“当前积压量”不够,因为积压量可能暂时不高,但生产速率已经持续高于消费速率。更重要的是判断消息是否具备优先级,核心订单消息是否会被低优先级报表消息挤占。

支付、物流、短信、身份认证和营销风控都可能是外部依赖。外部服务偶发变慢并不可怕,可怕的是核心接口没有设置合理超时,或者一次超时触发多次重试,导致线程和连接被长期占用。
重试策略必须考虑请求是否幂等。支付发起、库存扣减和订单创建不能简单地“失败就再来一次”。如果没有幂等键、状态机和补偿机制,重试可能产生重复订单、重复扣库存或重复支付风险。
我通常建议把外部调用拆成三个层级:
高峰期故障最浪费时间的场景,不是没有人处理,而是每个人都只看自己负责的模块。前端说接口返回慢,后端说数据库没有宕机,数据库团队说 CPU 不高,运维说机器健康,最终没有人对完整用户请求负责。
更有效的方式是按请求链路划分责任,并指定一个能够串联各团队的故障负责人。
| 链路环节 | 主要职责 | 必须提供的证据 | 常见误判 |
|---|---|---|---|
| 前端与客户端 | 请求合并、重试、资源加载、交互反馈 | 瀑布图、客户端耗时、重复请求数 | 把所有页面慢都归因于后端 |
| 网关与接入层 | 路由、限流、鉴权、负载均衡 | 节点流量、网关延迟、限流记录 | 只看全站平均流量 |
| 应用服务 | 业务逻辑、线程池、超时、降级 | 链路追踪、线程状态、接口分段耗时 | 只看 CPU 和错误日志 |
| 数据与缓存 | SQL、索引、锁、缓存和一致性 | 执行计划、连接等待、缓存命中和回源 | 数据库慢就只加索引 |
| 运维与 SRE | 资源、发布、监控、扩容和应急 | 变更记录、资源曲线、告警时间线 | 故障时只重启和扩容 |
没有基线,就无法判断一次变化是不是异常。每个核心接口至少要有流量、延迟、错误、依赖和资源五类基线。
例如,下单接口的基线不应只写“平均响应时间小于 500 毫秒”,还应记录在指定流量模型下的 P95、P99、超时率、数据库查询次数、锁等待和消息投递耗时。基线必须注明测试环境、数据规模、热点分布和是否包含第三方依赖。
基线不是永久不变的标准。商品数量增长、促销规则变复杂、数据库分片调整后,都需要重新校准。否则团队会拿两年前的性能目标衡量今天的系统,结论自然失真。
一次下单请求可能经过客户端、网关、订单服务、库存服务、数据库和消息队列。如果每个系统使用不同的日志标识,排障人员只能靠时间和参数猜测请求是否属于同一条链路。
统一请求 ID 不只是方便搜索日志,还应覆盖异步消息。订单请求产生消息后,消费者处理时应该保留原始业务 ID 和消息 ID,并记录生产、入队、消费开始、消费结束和失败重试时间。
没有这条链路,团队很容易出现“应用日志说成功,消息系统说延迟,订单状态却没有更新”的争议。统一标识能够把争议转化为时间线。
复盘文档如果只写“扩容后恢复”,下次遇到同类问题仍然要从头排查。有效复盘必须回答:第一个异常指标是什么、为什么没有提前告警、哪个资源被共享、临时方案为什么有效、长期方案如何验证。
我建议复盘至少包括以下内容:
如果团队使用数据分析工具汇总接口耗时、订单状态和业务转化,可以把技术指标和业务结果放在同一张看板上。例如,利用九数云这类数据分析工具,将网关日志、订单数据、商品热点和客服反馈进行关联分析,能够帮助团队观察“接口变慢是否真的影响了下单转化”。但这类工具更适合做跨数据源分析和趋势复盘,不能替代应用链路追踪、数据库监控和实时告警。

当请求量上涨时,CPU、内存、网络吞吐和磁盘 I/O 同时接近上限,并且接口吞吐不再随实例增加而线性增长,才更像是容量瓶颈。
行动顺序可以是:
扩容要有边界。新增应用节点后,如果数据库连接池按节点数量成倍增加,数据库可能从应用瓶颈变成连接瓶颈。因此,扩容前必须评估下游依赖的承载能力。
线程池排队而 CPU 不高,通常说明任务在等待。优先检查数据库连接、外部接口、锁、文件 I/O 和消息发送。
短期可以通过降低超时时间、限制重试、隔离线程池和关闭非核心调用止损。长期则需要缩短同步链路,把不影响用户即时决策的任务异步化。
这里要特别注意,降低超时不是简单地把所有接口改成 1 秒。超时时间应该结合业务可接受等待、下游正常 P99 和补偿机制设置。超时过短会增加失败率,超时过长则会占满线程。
这类问题常见于事务范围过大、连接未及时释放、批处理占用业务连接,以及连接池配置与应用并发不匹配。
建议检查:
如果连接等待远高于 SQL 执行时间,继续优化 SQL 的收益通常有限,应该先处理连接资源分配和事务边界。
先确认是全局命中率下降,还是某个业务 Key 的命中率下降。全局指标可能掩盖热点对象问题。
短期可以对热点商品做保护、延长缓存有效期、限制回源并发和启用降级数据。长期应建立缓存预热、过期时间随机化、热点识别和失效演练机制。
如果缓存存储的是库存、价格和优惠信息,还必须评估数据一致性。为了追求命中率而延长缓存时间,可能带来价格错误、库存显示不准或优惠计算异常。
消息积压不一定需要立刻扩容消费者。先区分积压消息的业务等级,确认是否有低价值任务占用消费者资源。
订单状态、支付结果和库存补偿通常优先级高于报表统计、推荐画像和营销分析。可以为不同类型消息设置独立主题、独立消费者或不同处理优先级,避免低优先级任务拖慢核心业务。
外部服务异常时,第一动作不是增加重试次数,而是确认请求是否幂等、是否有熔断、是否会占用核心线程。对非核心接口应优先降级,对关键交易应通过状态机和补偿任务保证最终一致。
如果支付结果查询超时,不能直接把订单判为失败;如果物流查询超时,也不应阻塞订单创建。业务状态设计决定了技术团队能否安全降级。

| 方案 | 优势 | 代价 | 更适合的情况 |
|---|---|---|---|
| 模块化单体 | 调用链短、部署简单、排障集中 | 独立扩容和团队边界较弱 | 业务规模中等、团队人数有限、边界尚未稳定 |
| 部分服务化 | 核心模块可独立扩容和隔离故障 | 需要处理网络、配置和数据一致性 | 订单、库存、支付等模块已有清晰边界 |
| 高度微服务化 | 组织和技术边界灵活、独立演进能力强 | 链路复杂、运维成本和排障成本高 | 多团队协作、业务规模大、平台工程能力成熟 |
我不建议把微服务作为性能优化的默认答案。服务拆分真正解决的是组织边界、发布边界和故障隔离问题,而不是天然提高单个请求的执行速度。
同步调用的优势是结果立即返回、业务流程直观,缺点是容易把下游耗时传导给用户。异步处理可以削峰和解耦,但会带来状态延迟、重复消费、消息丢失和补偿机制等问题。
判断一个动作是否适合异步化,可以问三个问题:
订单创建和库存扣减通常需要在核心交易流程中完成,但积分、推荐、埋点、营销统计和消息通知往往可以异步化。支付结果则要根据支付流程和一致性要求设计状态机,不能简单地归入同步或异步。
读写分离可以缓解主库读压力,但副本延迟会影响刚刚写入的数据读取。用户下单后立即查看订单,如果读请求被路由到延迟副本,可能出现“订单已创建但页面显示不存在”的体验。
因此,读写分离需要按业务场景设置一致性策略。交易确认、库存状态和支付结果通常需要更强的一致性;商品推荐、统计看板和部分列表查询可以接受短暂延迟。
扩容适合处理可预测的流量增长,降级适合处理突发超载和外部依赖异常。两者不是互相替代,而是应该配合使用。
如果只扩容而不降级,第三方服务或数据库仍然可能成为瓶颈;如果只降级而不扩容,正常核心请求也可能因为资源不足而失败。大促前需要明确哪些功能可以关闭、哪些功能必须保留,以及降级后用户看到什么结果。

这个阶段不宜只安排一次压测,而应先明确流量模型。至少要估算活动总用户、峰值并发、热点商品占比、读写比例、订单转化率和第三方调用量。
重点不是把所有接口都压到极限,而是验证最可能发生故障的环节。对爆款商品进行热点压测,观察库存行锁、缓存 Key 和数据库分片是否出现集中压力。
同时进行故障演练:关闭一个应用节点、降低数据库副本性能、模拟消息消费者变慢、模拟第三方接口超时,确认限流、熔断、降级和告警是否按预期生效。
大促前最危险的不是没有优化,而是临时修改了多个关键配置却没有验证。应建立变更冻结窗口,只允许有负责人、有回滚方案、有监控验证的紧急变更进入生产。
重点检查:
高峰期最忌讳多人同时修改配置。每次动作都应记录时间、目标、预期指标和回滚方式,并在短时间内观察 P99、错误率、连接等待和业务转化是否改善。
如果扩容后 CPU 下降但 P99 没有变化,说明瓶颈可能在下游;如果限流后错误率下降但支付成功率也下降,说明限流规则误伤了核心用户;如果关闭推荐后系统恢复,应该继续追查推荐调用为何能够占用核心资源,而不是把关闭功能当成永久方案。
系统恢复并不等于故障结束。还要确认消息是否全部消费、订单状态是否一致、库存是否需要补偿、支付回调是否存在遗漏,以及缓存中的价格和活动状态是否已经更新。
很多隐性故障会在活动结束后才暴露,例如消息积压导致订单通知延迟、数据库主从延迟导致后台数据不一致、补偿任务重复执行导致库存异常。收尾阶段同样需要监控和负责人。

监控数量多不等于可观测性强。如果接口、应用、数据库和消息系统之间没有统一请求 ID,团队仍然无法回答“这批慢请求到底慢在哪里”。真正有效的监控应该能够从用户请求下钻到具体服务、SQL、Key、消息和外部调用。
我会随机抽取一批慢请求,验证能否在几分钟内回答以下问题:请求经过了哪些服务、每个环节耗时多少、是否发生过重试、使用了哪个数据库节点、是否等待锁、最终业务结果是什么。
抗高峰不是所有功能永远可用,而是在资源不足时优先保证下单、支付、库存和订单状态等核心功能。推荐、报表、画像、营销统计等非核心功能可以延迟或降级,但降级必须是事先设计的,而不是故障时临时删除接口。
系统需要明确优先级和资源边界。没有优先级的系统,通常会出现所有请求一起排队,最终核心和非核心功能同时失败。
如果每次故障都靠几个资深工程师临时判断,系统并没有真正变稳定。稳定性能力应该沉淀为基线、告警、演练、复盘、自动化检查和明确的负责人。
团队精细化并不意味着增加更多审批和会议,而是让每个动作都有证据、有边界、有验证。开发人员知道看哪些指标,运维人员知道何时扩容,数据库人员知道何时处理锁等待,产品和业务人员知道哪些功能可以降级,故障处理才会从“靠经验”变成“按流程协作”。
如果你的系统最近遇到高峰期卡顿,不建议一开始就重构架构。先选择一个最关键的用户动作,例如“提交订单”,把它从客户端到数据库完整画出来,并为每个环节补齐耗时和失败指标。
接下来按以下顺序执行:
电商系统高峰期卡顿的根因,通常藏在“资源没有打满但请求已经排队”的地方。真正专业的开发团队,不是永远不出问题,而是能够快速看见等待发生在哪里,知道哪些功能必须保住,知道每一次优化带来了什么副作用,并把一次故障转化为下一次高峰期的确定性。
我们团队做过一次大促前压测,发现下单接口变慢时,应用服务器 CPU 只有 58%,内存也很稳定。最初大家都以为是机器配置不足,扩容后却没有明显改善。我想知道,遇到这种“资源没打满但接口很慢”的情况,正确的排查顺序到底是什么?
不要把 CPU 使用率当成系统是否卡顿的唯一依据。电商系统更常见的瓶颈是线程池排队、数据库连接池耗尽、锁等待或外部接口超时,这些问题发生时,服务器可能仍然处于“看起来不忙”的状态。
我们在一次脱敏压测中观察到:下单接口平均响应时间从 180 毫秒升到 620 毫秒,P99 从 1.2 秒升到 8.6 秒,但 CPU 仅从 51% 上升到 58%。继续查看指标后,发现数据库连接池使用率达到 98%,获取连接的平均等待时间超过 2 秒,慢查询数量也同步增加。
排查对象重点指标典型判断 应用服务器CPU、内存、GC、线程数资源是否真的达到上限 线程池活跃线程、排队线程、拒绝数请求是否卡在应用内部排队 数据库连接池活跃连接、等待时间、超时数接口是否拿不到数据库连接 数据库慢 SQL、锁等待、长事务查询慢还是资源争抢 外部依赖连接耗时、读超时、重试次数慢请求是否被第三方接口拖慢 更稳妥的顺序是:先确认用户到底慢在哪个接口,再查看网关耗时、应用耗时和下游依赖耗时,最后进入数据库、缓存和外部服务。
建议同时关注平均响应时间、P95、P99、超时率和错误率,因为平均值很容易掩盖少量但严重的长尾请求。扩容适合解决明确的计算或容量不足,例如 CPU 持续超过 85%、请求吞吐已接近压测上限。
但如果根因是锁等待、慢 SQL或连接池配置不合理,继续加机器往往只是把问题分散,甚至会让数据库承受更多并发连接。
我们曾遇到过商品详情页在活动开始后突然变慢,数据库监控显示查询量暴涨,团队一度准备给数据库扩容。后来有人发现缓存命中率在几分钟内快速下降。我想知道,缓存失效、热点 Key 和数据库慢查询在监控上分别有什么特征?
判断缓存问题,不能只看“缓存命中率低”这一项,而要把命中率、回源 QPS、热点 Key、Key 过期时间和数据库查询量放在同一条时间线上观察。缓存命中率下降只是现象,真正需要确认的是:大量请求是否在同一时间穿透到了数据库。
在一次典型场景中,商品详情接口的缓存命中率从 96% 降到 71%,数据库 QPS 在 90 秒内增加约 3.4 倍,但数据库 CPU 只从 46% 升到 63%。继续检查发现,大量商品缓存使用了相近的固定过期时间,活动开始后出现集中失效,数据库并没有先变慢,而是先被回源流量冲击。
现象优先观察更可能的根因 命中率突然下降,回源 QPS 同步上升Key 过期分布、回源请求量缓存集中失效或缓存击穿 只有少数商品接口极慢单 Key 访问量、热点分布热点 Key 或大促爆款集中访问 命中率正常但接口仍然慢序列化耗时、缓存网络延迟缓存对象过大或缓存服务本身拥堵 数据库查询持续变慢慢 SQL、锁等待、索引命中数据库执行计划或事务竞争问题 我更建议把缓存过期时间做随机化,避免同一批数据在同一秒失效;
对高频热点数据设置互斥更新、逻辑过期或请求合并;对商品详情这类读多写少的接口,还可以在活动前进行定向预热。但这些措施不能替代数据一致性设计,库存和支付状态不能简单依赖长时间缓存。一个实用判断方法是对比“缓存命中率下降的时间点”和“数据库慢查询出现的时间点”。
如果先出现命中率下降和回源暴涨,数据库变慢通常是结果;如果命中率稳定,但慢 SQL、锁等待和事务时间先上升,根因更可能在数据库查询或业务事务设计。
我们看过一份接口报表,平均响应时间只有 300 毫秒,团队据此认为系统性能正常。但用户反馈集中在活动高峰期,部分订单点击后要等待 6 到 10 秒。我想知道,平均值为什么会掩盖问题,开发团队应该用哪些指标来判断真实体验?
平均响应时间适合观察总体趋势,却不适合判断高峰期的用户体验。假设 99 个请求耗时 100 毫秒,1 个请求耗时 10 秒,平均值约为 199 毫秒,看起来并不严重,但那个 10 秒请求对应的用户已经可能重复点击、刷新页面,甚至重复提交订单。
在我们做过的一次接口分析中,下单接口平均耗时为 340 毫秒,P95 为 1.1 秒,P99 为 7.8 秒,超时率为 0.7%。如果只看平均值,系统似乎表现良好;但从用户行为看,接近 1% 的请求已经足以造成明显投诉,尤其是在支付、库存和订单确认等关键链路上。
指标回答的问题适合的用途 平均响应时间整体请求大致耗时多少观察长期趋势 P95较慢的 5% 请求表现如何评估大多数用户体验 P99最慢的 1% 请求是否异常定位长尾延迟 超时率有多少请求没有在规定时间内完成衡量可用性风险 重复提交率用户是否因无反馈而再次点击连接技术问题与业务损失 开发团队应为核心接口分别建立平均值、P95、P99、错误率和超时率基线,而不是只设置一个平均响应时间告警。
对下单、支付和库存扣减,还要结合业务结果观察,例如订单创建成功率、库存扣减失败率、支付状态回调延迟和重复订单数量。排查长尾请求时,要进一步拆分单次请求的耗时构成:网关耗时、应用计算耗时、数据库耗时、缓存耗时、消息投递耗时以及第三方调用耗时。
实践中,P99 异常往往不是所有请求都变慢,而是少量请求被锁等待、连接获取或外部重试拖住。
过去我们做过一次大促演练,压测报告写着“系统通过测试”,但正式活动时消息队列仍然持续堆积,部分营销接口还影响了下单链路。我想知道,压测通过到底意味着什么?开发团队在上线前应该检查哪些容易被忽略的风险?
“通过压测”不是一个完整的结论,必须说明测试流量模型、持续时间、接口比例、数据规模、依赖服务状态和出现瓶颈的先后顺序。如果只用单接口、固定参数和空数据库做压测,得到的结果通常不能代表真实大促。
我们在一次演练中发现,核心下单接口在每秒 800 次请求下仍能保持稳定,但营销规则查询和订单报表任务共用数据库连接池。活动开始后,非核心查询占用了大量连接,导致下单接口 P99 从 900 毫秒升到 5.4 秒。问题不在系统总吞吐量,而在核心与非核心资源没有隔离。
检查阶段必须验证的内容常见遗漏 流量建模接口比例、峰值、突发流量、持续时间只压首页,不压下单和库存 资源隔离线程池、连接池、数据库与队列配额营销、报表挤占交易资源 故障演练限流、降级、熔断、回滚只测试正常情况 异步链路生产速率、消费速率、积压恢复时间只看队列是否能接收消息 监控告警P99、超时率、锁等待、消息延迟只有 CPU 和内存告警 高峰期前至少要确认三件事:核心链路能否独立获得资源,非核心功能是否可以降级,以及故障发生后能否在几分钟内完成止损。
比如推荐、画像、报表和部分营销计算可以异步化或暂时关闭,但库存扣减、订单创建和支付状态处理必须有明确的保护策略。容量评估还应记录“哪个模块先到达瓶颈”。如果在每秒 1000 次请求时数据库锁等待先升高,就不能简单地把系统容量写成“支持每秒 1000 次”;
更合理的结论应是:在当前数据规模、接口比例和事务设计下,系统可稳定承载某个范围的交易流量,超过该范围需要限流、拆分资源或优化事务。上线前建议形成一页纸的应急清单,写清楚告警阈值、关闭开关、回滚版本、值班负责人和升级路径。
真正可靠的高峰期保障,不是压测报告里写一句“通过”,而是团队知道异常出现后先看什么、谁来处理,以及如何避免非核心问题扩散到交易链路。


读者评论
文章对“CPU不高但系统变慢”的解释比较到位,尤其是把连接池排队、锁等待和线程阻塞区分开来,对实际排障有参考价值。不过文中部分数据属于情景模拟,落地时仍需结合自身链路监控验证。
从开发团队角度看,缩短事务边界、拆分读写职责和隔离连接池这些建议较实用。相比单纯扩容,更强调定位共享资源瓶颈,适合有促销活动和热点商品场景的电商系统。
文章对压测模型不完整和平均响应时间误导的问题分析得比较客观。除了关注P99,还应把热点商品、库存冲突、重试行为纳入压测,否则单接口通过并不能代表完整下单链路稳定。