
1. 高并发问题的本质面试官到底在考察什么“高并发”是后端技术面试里出现频率最高的词之一。只要简历上写了相关项目几乎都会被问“你的系统怎么抗住高并发流量”这个问题表面问的是架构方案实际上考察的是技术深度、项目真实性和系统设计能力。我面试别人的时候最怕听到的答案不是错而是“我们用了缓存加消息队列做削峰”——这句话没有任何信息量因为它没有回答“为什么用”“什么时候用”“不用会怎样”。1.1 高并发不只是“请求量大”很多人第一步就理解偏了。高并发到底高在哪里如果只是请求量平稳上涨最简单的办法是加机器。真正要命的是流量在短时间内达到峰值并且形成热点。举个例子某个普通业务白天每秒1000个请求晚上8点整突然涌进来5万请求持续10分钟。这10分钟里如果系统扛不住前面所有优化都白搭。所以高并发问题的本质不是“量大”而是“在有限资源下让关键链路在峰值流量下的表现依然可控”。这里有一个核心事实系统能承受的并发量不取决于最强的节点而取决于最弱的节点。用户发起一次请求链路可能是“接入层 → 业务逻辑 → 缓存 → 数据库”。哪怕缓存很快数据库只要出现一次慢查询或连接池耗尽整个接口的响应时间就会迅速恶化。我习惯在分析任何系统前先画一条调用链标出每一环的预估耗时和容量上限找出最弱的那一个。面试中如果能说出来“瓶颈可能在某一层”就比背方案强很多。1.2 面试官真正想听的是权衡和取舍给一个系统做高并发设计本质是“在成本、复杂度、一致性之间做取舍”。没有完美的方案。比如加缓存能提升读性能但会引入缓存与数据库的一致性问题引入消息队列能削峰但会多出消息丢失和重复消费的风险分库分表能扩展存储和写入能力但会让查询和事务变得非常难做。面试官希望你展示的是你知道每个方案的收益也知道它的代价并且能根据业务场景选择合适组合。我面试时通常会追问“如果缓存挂了怎么办”“消息队列积压了怎么处理”“数据从单库拆到多个库之后跨库查询怎么做”这些问题背后只有一件事你是否真在线上环境踩过坑。如果你只是背过“缓存穿透、击穿、雪崩”却没有对应场景的排查经验回答会非常空洞。所以后面几节我会按一条真实项目的演进路径从单机优化开始一直到规模化架构把每个环节的技术选型、实操细节和踩坑经验都摊开讲。2. 抗住高并发的第一层防线架构拆分与无状态化2.1 单机容量先算清楚动手改造前必须知道自己现在能扛多少。QPS每秒请求数、RT平均响应时间、并发数三者有一个基本关系并发数 ≈ QPS × RT秒。举个例子一个接口平均响应时间200ms如果要以每秒2000个请求的速率处理那么系统里同时处理的请求数约为2000×0.2400个。如果单机只能支撑300个并发连接瓶颈就出现了。这个公式在容量规划时非常好用面试时直接拿出来讲比“我们系统QPS很高”有说服力得多。怎么测单机容量搭一台干净的测试机用压测工具逐步加压观察CPU、内存、线程数、GC停顿和响应时间。我这里有一个简单判断标准当QPS不再线性增长或RT开始指数级上升就是这台机器的拐点。记录下拐点数据后续做容量规划和水平扩展才有依据。很多团队没有这个基线大促前临时加机器结果加了机器流量还是进不来因为瓶颈根本不在应用层。2.2 横向扩容的前提无状态化改造很多人以为“加机器”是万能的实际上加机器之前必须先做无状态化。什么叫有状态用户登录后会话信息存在某台应用服务器的内存里负载均衡把下一次请求转发到另一台服务器用户就需要重新登录。这就是有状态带来的问题。无状态化就是把会话、上下文数据从业务节点中剥离放到外部存储中让每一台应用服务器都能处理任意请求。具体做法包括会话信息改为令牌保存或者把会话数据放进集中式缓存定时任务避免在每台机器上重复执行可以引入分布式锁本地缓存可以保留但要知道它不是全局一致。只有先做到无状态后续扩容才能做到“随时加一台机器、随时摘掉一台机器”。面试时如果只谈“扩容”不谈“无状态”面试官通常会认为你做过的项目规模不大。2.3 接入层的负载均衡不是随便挂一台就行负载均衡层承担着流量入口的角色。常见做法是先用DNS做地域级分流再用负载均衡器做单地域内的流量分发。但配置里有很多细节很多人忽略健康检查的超时时间如果设得太长后端节点已经故障前端还在继续转发请求会加重故障均衡算法如果一律用轮询很快会发现性能差的机器被拖垮。我推荐优先使用最少连接数算法或者结合后端节点的实时权重。此外连接超时和读写超时一定要分开设置。举个例子如果应用处理一个请求平均要200ms可以把读超时设为3秒而不是保持默认的30秒。否则某个数据库慢查询导致应用线程阻塞时负载均衡器并不会及时摘除这个节点用户感知到的是整个接口长时间无响应。这些细节在面试中讲到会让人觉得你是在真实环境里调过的。3. 缓存与异步把压力挡在数据库之前3.1 缓存设计必须处理的三个经典场景缓存是提升读性能最直接的手段但设计不当反而会成为事故源头。我每次审查缓存方案会先看三个问题缓存穿透、缓存击穿、缓存雪崩。穿透是指查询一个根本不存在的数据缓存和数据库都没有查询直接打到数据库。如果被恶意利用会用大量不存在的ID打垮数据库。解决办法很简单对空结果也做缓存比如给一个默认值缓存几十秒或者用布隆过滤器在缓存前拦截。布隆过滤器有一定误判率但能挡掉绝大多数不存在请求。击穿是指某个热点Key在缓存过期的一瞬间大量请求同时打到数据库。处理思路是热点数据的过期时间尽量分散或者用互斥锁控制只有第一个请求去回源其余请求等待缓存刷新。我在实战里更常用的办法是对热点Key不过期改为后台异步刷新并加上失效兜底。这个方案需要额外维护一个刷新任务但能彻底避开“缓存过期那一刻”的雷。雪崩是指大量Key在同一时间失效导致数据库瞬时压力暴涨。解决方法包括过期时间增加随机偏移必要时做多级缓存本地缓存挡一层集中缓存挡一层数据库兜底。聊到雪崩时我还会补一句“降级预案”如果缓存整体不可用能不能直接返回旧数据或者短暂降级没有预案的缓存方案在线上是不完整的。3.2 异步削峰消息队列不是用来“提速”的很多人把消息队列理解为“处理得更快”这是误解。消息队列的真正价值是削峰填谷——把突发流量先接住再按下游的处理能力匀速消费。就像水库平时蓄水洪峰来的时候拦住下游渠道不会被冲垮。典型的场景是秒杀、下单、日志上报。用户在入口拿到一个“排队成功”的返回后续的订单处理和库存扣减都在后台异步完成。引入消息队列之后一定要回答三个问题第一消息丢失怎么办生产端开启确认机制消费端处理成功后再提交偏移量能做到基本不丢。第二消息重复消费怎么办消费逻辑要最好具备幂等性或者用唯一业务ID做去重。第三消息积压怎么办需要监控消费积压量并准备扩容消费者的方案。面试时很多人只讲“削峰”不讲“这三种问题”你一说出来深度立刻不一样。3.3 限流、降级、熔断流量失控时的安全网高并发系统不能只考虑“扛得住”还要考虑“扛不住时怎么优雅地失败”。限流是为了控制进入系统的流量速率降级是为了在资源紧张时放弃非核心功能熔断是为了在下游故障时快速失败而不是无限等待。这三者经常配合使用是隔离故障的关键。我常用的限流方案是令牌桶以固定速率往桶里放令牌请求进来先取令牌取不到就快速失败。相比简单计数器令牌桶能容忍一定的突发流量更适合业务入口。限流阈值不能拍脑袋定要基于压测数据。比如压测得出下单接口最大安全QPS是3000那么限流阈值可以设为2500留出20%左右的余量。降级和熔断的思路类似都要靠开关控制和阈值触发。我常说的原则是“核心链路保底非核心功能可断”例如推荐列表挂了不能让下单流程进不来。4. 数据层抗压数据库与存储的极限拉扯4.1 读写分离与分库分表的边界条件数据库往往是高并发系统的最后一道瓶颈。所有优化最终都会落到“减少数据库压力”这一件事上。读写分离是最先考虑的手段主库负责写从库负责读业务上把读流量从主库剥离。但要注意从库有主从延迟读写分离后立刻面临“写完读不到”的问题。我的经验是实时性要求高的逻辑继续走主库可以接受秒级延迟的查询走从库。当读写分离不够用时才考虑分库分表。分库分表不是目的而是手段。它解决的是两个问题单库连接数不够单表数据量太大导致索引效率下降。分表键的选择极其重要通常选用户ID或订单ID这类自然分布均匀的字段。选错分表键的代价是灾难性的比如按店铺分表但用户查询需要跨所有店铺聚合复杂度直接爆炸。我见过某团队把订单表按用户ID分成256张表后续所有按订单号查询都只能走字段映射等于每次先查路由表明显得不偿失。4.2 索引、连接池、慢查询数据库侧的隐形瓶颈有时候数据库CPU不高、连接数也正常但接口就是慢。这种问题多数藏在索引和连接池里。索引失效是高频事故对索引字段做了函数运算或者隐式类型转换优化器都可能放弃索引。面试时我会拿一个具体SQL举例状态字段是整数但查询条件传了字符串数据库会做隐式转换结果每行扫描一次。这种问题靠“多建索引”解决不了得从查询模式反推索引设计。连接池也是一个需要精调的地方。设置过大会导致数据库线程数膨胀、上下文切换严重设置过小又会导致请求排队。一般服务初期连接池上限设置为50100就够用但很多项目一上来就配200反而把数据库拖垮。连接池的等待超时也需要单独设置宁可让业务快速失败也不能让请求无限阻塞。慢查询是数据库问题中最好定位的开启慢查询日志定期把高频慢SQL捞出来分析。排查时我习惯先看执行计划确认是否走了索引再看表行数和数据分布。很多慢查询并不是语句写得差而是表数据量到了千万级索引区分度下降这时候就要考虑归档或分表。4.3 分布式事务和一致性问题怎么聊高并发一定会引入分布式场景订单在A服务创建库存扣减在B服务积分在C服务。这时候分布式事务是无法回避的话题。面试中最安全的回答方式不是宣称自己用了强一致方案而是强调“能避免跨服务事务就避免避免不了优先用最终一致性方案并为对账和补偿保留余地”。强一致方案如两阶段提交性能开销很大高并发场景基本不适合。比较务实的做法是本地消息表加消息队列业务操作与消息写入放在同一个本地事务里消息由异步任务或消息队列投递给下游消费方做幂等处理。这套方案实现简单而且能保证最终一致。更精细的做法是引入事务消息但需要消息中间件的支持。面试时能把这个演进链路讲清楚比单纯说“我们用了分布式事务框架”要好很多。重点不是扣数字而是让面试官看到你对一致性的理解有层次。5. 容量评估与压测怎么知道自己还能扛多久5.1 三个关键指标QPS、RT、错误率判断一个系统处于什么状态我会同时看三个指标QPS、RT、错误率。很多人只看QPS涨了多少却不知道RT已经翻了十倍错误率也在悄悄上升。用前面提到的公式“并发数 ≈ QPS × RT”来推导在并发数不变的情况下QPS上升必然导致RT上升。如果压测时观察到RT明显拐头那就说明系统已经进入过载状态不是单纯的流量波动。错误率同样重要。我通常把错误率红线设为0.1%压测中超过这个值就要停下来找原因。常见的错误包括连接超时、连接被拒绝、线程池满、数据库连接池满。每一种错误的解法都不同但如果错误率和RT同时飙升十有八九是某个下游依赖或共享资源被打满。这时候优先排查数据库和缓存然后是应用线程池。5.2 压测的实操方法和常见误区做压测之前先把压测环境隔离出来。不要在正式环境直接压也不要和生产库共用同一台机器否则压测流量会把生产任务拖慢。逐步加压是关键从低并发开始观察各项指标变化找到拐点。不要一上来就用一万并发那样只能看到结果看不到过程。另一个常见误区是压测只测成功路径。真实的用户请求包含各种分支比如鉴权失败、参数校验失败、缓存未命中。只压“性能最好的那条路径”得出的容量上限是虚高的。我习惯把核心接口的典型请求比例做成压测脚本尽量模拟真实流量分布。压测数据也不是只出一份报告就完事而是要把它沉淀成容量基线每次上线前对比一次。不同版本的代码、配置、机器规格都会影响容量基线要持续更新。5.3 容量规划与预案设计容量规划的核心是给未来留余量。如果普通日QPS是1000大促预估是10倍那么容量至少要按20倍去设计多出来的10倍是给流量毛刺和故障准备的。估算方法不复杂确定目标QPS乘以平均RT得到需要支持的并发数再除以单机容量算出需要多少节点。注意节点数要留冗余不要算得刚好否则一台机器宕机就满盘皆输。预案比预测更重要。我会为大促场景准备三类预案扩容预案、限流预案、降级预案。扩容预案明确在多少QPS时加机器限流预案定义哪些流量可以被丢弃降级预案划清核心链路与非核心链路的边界。日常就要演练不能等线上出问题再临时写方案。高并发能力不是上线那天才有的是反复压测、演练、复盘堆出来的。6. 高并发系统常见问题排查与面试表达建议6.1 线上高并发问题排查速查表我把线上高频问题和对应排查方向整理成一张表每次排障都从这里入手。现象最可能原因排查方向整体RT升高数据库慢查询或连接池耗尽慢查询日志、连接池指标、数据库线程数部分节点RT高负载不均衡或某节点GC频繁负载均衡策略、GC日志、节点健康状态缓存命中率骤降大量Key过期或缓存服务抖动缓存命中率监控、过期时间分布、缓存日志消息消费延迟消费者数量不足或消息分区不均消费积压指标、分区分配情况错误率上升下游依赖超时或线程池满熔断状态、超时配置、线程池队列数据库CPU打满慢SQL集中执行或热点行写冲突数据库SQL统计、热点行锁等待这张表并不能解决所有问题但能帮你快速定位方向。排障时最重要的原则是“先恢复再定位”。遇到线上故障先通过降级、摘节点、扩容量恢复服务再去复盘根因。我曾经遇到一次事故花两小时分析代码最后发现只是缓存集群连接数配置错误。如果先摘掉异常节点流量早就恢复了。这个教训我一直记着。6.2 一套不容易翻车的回答框架如果被问到“怎么让系统抗住高并发流量”我建议按“确认指标 → 分析瓶颈 → 分层优化 → 验证结果”四步回答。先问清楚业务场景是读多写少还是写多读少峰值流量是平时的多少倍然后讲瓶颈分析链路里哪个环节最弱最后再讲优化方案并且每说一个方案同时说它的副作用和适用条件。我举个例子。假设是商品详情页读多写少峰值是平时10倍。回答思路可以这样先做无状态化多节点水平扩展接入层负载均衡对详情数据做缓存处理好穿透、击穿、雪崩对非核心数据做异步加载数据库做读写分离压测确认单机容量后按20倍目标容量预留节点再加限流降级兜底。面试官继续追问任何一环你都能往下讲。这套框架不一定惊艳但一定能让面试官觉得你是体系化思考过的而不是背了一堆名词。最后再分享一个真实体会。高并发改造最难的往往不是技术本身而是“你以为你改了但流量根本不到那么大”。我在前几年接手一个项目时团队天天讨论加缓存、上消息队列后来压测发现单机其实能扛住预期流量的三倍根本不需要分布式架构。真正上线后出问题的反而是日志框架和第三方接口超时。所以我的建议是不要为了架构而架构先量化再优化。把压测工具用熟、把监控指标看会比背一百个架构名词都有用。