ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

晚高峰几十万并发:高并发系统架构设计与治理实战

晚高峰几十万并发:高并发系统架构设计与治理实战 晚高峰几十万并发这个题目很多团队不是被真实流量打垮的而是被自己人打垮的——告警风暴淹没核心故障、缓存击穿直接打穿数据库、日志框架把CPU吃满、限流误伤支付回调。这些次级灾害才是高并发系统的真正杀手。我经历过多次大促前的压测和线上的晚高峰这篇文章把应对几十万并发的系统设计思路、分层架构、写链路保护、读链路治理、容量评估和故障复盘完整拆开来讲适合正在做电商平台、准备做高并发改造的后端工程师和技术负责人参考。1. 先看透流量真相晚高峰几十万并发的底层特征做高并发架构之前第一件事不是选什么中间件而是把流量摸清楚。几十万并发这个数字听起来很吓人但拆开看晚高峰的流量有非常鲜明的规律每一步判断都建立在这些规律之上。1.1 QPS和并发之间隔着一个秒杀率很多人混淆并发数和QPS这两个概念的区别直接决定了你的预估模型和系统容量设计。并发数是某个瞬间系统正在处理的请求数量QPS是每秒钟能处理的请求数量。这中间隔着一个关键变量——单个请求的平均耗时。如果每个请求平均耗时200ms一台机器单核同时只能处理一个请求那么这台机器单核的QPS大约是1000/200也就是5QPS。一台16核的机器理论QPS大约80。如果同样的逻辑优化后平均耗时降到50ms同样的机器QPS就能飙到320。所以并发几十万真正要算的是QPS和平均响应时间这两个参数才是设计容量、评估机器数的依据。电商晚高峰的流量模型是典型的双峰形态晚上八点到十点有两个明显的波峰一个在八点前后的开抢时段一个在九点半左右的截单前冲刺。每个波峰持续大概十到二十分钟期间QPS会突然拉升到平峰的5到8倍然后快速回落。这跟秒杀场景不一样秒杀是瞬间的尖峰通常持续几秒到几十秒晚高峰的波峰是宽峰持续时间长对系统的压力是持续性的更需要关注系统的稳定性而不是单纯的瞬时吞吐。1.2 晚高峰流量画像读多写少、热点集中、链路长电商平台的核心交易链路是浏览商品、加购、下单、支付、查单、物流跟踪。这条链路的流量分布极其不均读操作占了绝大多数。以我经历过的线上数据为例详情页和搜索结果页的QPS占比大概是70%到80%真正落到订单创建的写操作QPS不到总量的5%支付回调又比下单少一个量级。这就是经典的读多写少模型。流量画像还有一个显著特征是热点集中。晚高峰的流量并不是均匀分散在所有商品上的往往集中在首页推荐位、爆款商品、活动会场和直播间这几个地方。一个爆款详情页的QPS可能占全站详情页总QPS的30%以上。这就是热点数据倾斜如果不做特殊处理一个热点Key就把单台缓存节点或者单个数据库分片打满而其他节点还在空闲。流量画像决定了系统设计的重心——先解决读的扩展性再解决写的可靠性最后才是全局的一致性。1.3 按流量画像锁定系统瓶颈顺序我做了这么多轮容量评估和压测发现一个规律瓶颈几乎永远按同一个顺序出现。第一个瓶颈是数据库连接数。数据库的连接池是有上限的比如一个MySQL实例默认最大连接数1536。假设每个服务的连接池设置50个连接20个服务实例就能把一个数据库的连接池吃穿。流量一上来最先告警的永远是too many connections。第二个瓶颈是带宽和连接数。很多人只关注CPU和内存忽略了网卡带宽。有一次压测我们以为瓶颈在应用层结果发现单台机器的出口带宽被打满了应用CPU才用了30%。大促前的容量评估必须把带宽算进去尤其是有大图片、大JSON响应的接口。第三个瓶颈才是CPU和内存。服务本身的逻辑复杂程度、GC停顿、线程上下文切换这些是流量大到一定程度之后才暴露的。特别是Java应用大促期间频繁Full GC导致的长耗时和超时比数据库瓶颈更难排查。基于这个顺序架构设计的优先级就清楚了先把流量挡在前面接入层缓存和限流再把热点数据挪到离用户最近的地方CDN和本地缓存最后才轮到数据库和业务逻辑的优化。如果一开始就埋头优化业务代码的算法复杂度方向就反了。2. 分层架构实战从接入层到数据层的分工与选型很多团队一谈高并发就上微服务、上各种中间件结果把系统拆得乱七八糟性能反而更差。我的做法是先明确每一层要解决什么问题选型的理由能写清楚再动手改架构。2.1 接入层与网关层四层负载和七层路由怎么配合晚高峰几十万QPS落在入口靠单一Nginx扛不住需要分层分流。最外层是四层负载均衡通常是LVS或者云上的SLB工作在TCP/UDP层只做IP和端口级别的转发不解析HTTP内容所以性能极高。一台LVS可以扛住上百万的并发连接。它负责把流量分摊到后面的Nginx集群上。Nginx这一层做七层负载解析HTTP协议可以做URL路由、HTTPS卸载、静态资源缓存、限流。Nginx用的是epoll事件驱动模型单worker进程可以处理数万并发连接但动态请求的QPS能力受限于上游应用的响应速度通常几千到一万多。接入层的选型经验是四层负载一定要做高可用主备模式加上健康检查避免LVS本身成为单点Nginx集群按照业务域拆分成多个server块比如商品域、订单域、用户域各一组隔离故障边界。网关层的限流在这里就要开启比如全局限流、用户维度限流、接口维度限流避免单个异常流量把整个入口打垮。Spring Cloud Gateway这类微服务网关通常放在Nginx之后、业务服务之前。它承担的是路由转发、鉴权、灰度发布、聚合调用等功能。要注意的是网关不要做太重的东西不要在网关里写业务逻辑不要把网关当成缓存层、不要在这层做数据组装。我在真实项目中见过把商品聚合接口放在网关里的做法结果网关变成了性能瓶颈和故障扩散点一个下游抖动网关线程池满整个服务全挂。2.2 业务服务层的拆分粒度与无状态化服务拆分不是越细越好。拆得太细调用链路过长一个请求要经过五六个服务晚高峰的链路延迟和失败率都会上来。我的实践原则是先按业务域拆再按流量拆。第一刀按业务域拆分商品、库存、订单、支付、用户、营销每个域一个或多个独立服务。第二刀是当某个域内部出现明显的性能瓶颈或者团队协作冲突时再按子域或者核心功能拆分。比如订单域流量大可以拆出订单查询服务和订单写服务读和写分离各自独立扩缩容。服务无状态化是水平扩容的基础。所谓无状态就是服务实例本身不保存用户会话、不保存本地缓存之外的关键数据、不依赖本机磁盘上的文件。会话信息放到Redis分布式缓存中的数据允许短暂不一致本机临时文件用对象存储替代。做到这一点流量高峰来了才能随时往后面加机器而且每台机器都是等价的负载均衡才能均匀分发流量。微服务之间的调用我最常用的还是同步HTTPOpenFeign配合异步MQ的组合。核心交易链路需要实时返回结果用同步调用但必须设置超时和重试上限通常设置500ms连接超时、3s读超时重试不超过1次防止雪崩。非核心流程比如发送短信、更新用户积分、记录浏览历史、生成推荐日志全部走MQ异步化削峰填谷让尖峰流量变成平稳的消息流下游消费端按自己的节奏处理。2.3 数据层的缓存、分库分表与存储选型数据层是最后一道防线也是最难扩展的一层。数据库的容量的扩展不像应用层加机器那么简单需要提前一到两个月规划。缓存先行。Redis是核心晚高峰所有的读流量80%以上都应该命中在Redis而不是打到数据库。Redis的QPS单实例可以到十万以上但高并发场景下必须用集群模式。我用的是Redis Cluster数据按Hash Slot分布到多个节点每个节点一主一从或者一主多从。热点数据通过Hash Tag的方式路由到同一个槽位但要注意Hash Tag导致的数据倾斜问题后面有专门的章节讲。数据库的分库分表电商场景通常先按业务分库再按用户维度分表。订单表、支付流水、购物车记录都是典型的用户维度数据可以按用户ID的Hash值分表。比如用userId mod 128把数据均匀分布到128张表。商品表是全局数据不适合按用户分可以按商品ID分片。分库分表之后跨分片的查询和事务会变得复杂所以要提前想好哪些查询必须支持跨分片哪些可以走汇总库、宽表或者搜索引擎。存储选型上MySQL还是绝对的核心存储用于事务性数据。Redis是缓存也是带持久化的存储但不是所有数据都适合进Redis。ES用于商品搜索和日志检索。消息队列选型我倾向于RocketMQ或者KafkaRocketMQ的事务消息、延迟消息能力更适合电商交易场景。这些选型不是越新越好而是看团队熟悉度和生态成熟度。3. 写链路保护库存、订单、支付的防超卖与幂等设计读链路可以通过加缓存、加机器来解决真正的架构功力体现在写链路。库存扣减、订单创建、支付回调这三段是最容易出事故的环节也是晚高峰最不能出错的地方。3.1 库存扣减的三种方案演进从数据库行锁到Redis Lua库存扣减的技术方案经历了三个阶段每个阶段都有明确的取舍晚高峰需要哪种取决于预期峰值和一致性的容忍度。第一个阶段是数据库行锁方案。伪代码逻辑是select stock from t_sku where id ? for update拿到行锁后判断库存是否充足再执行update。这个方案的优点是强一致、实现简单缺点是行锁导致并发度极低。一个SKU的库存被扣减时其他请求全部阻塞等待。压测过单行记录在InnoDB下的最高并发写QPS只有几千离晚高峰几十万的量级差得太远秒杀场景更是不可能。第二个阶段是乐观锁方案。update语句带上版本号或者剩余库存条件SQL写法是update t_sku set stock stock - ? where id ? and stock ?影响行数为0说明扣减失败业务层直接返回已售罄。这个方案避免了显式加锁并发度比行锁高很多单行QPS能到几万但仍然直接打在数据库上数据库的压力和Binlog的写入压力还是很大。第三个阶段是Redis Lua脚本方案也是大促主流方案。把库存预加载到Redis扣减逻辑在Redis端用Lua脚本原子执行。脚本核心逻辑是先判断库存是否充足充足则扣减不足则返回失败。因为Lua脚本在Redis单线程内执行天然原子不会出现并发超卖。这个方案可以支撑极高的QPS单Redis节点的Lua扣减QPS可以到几万以上配合Redis Cluster可以水平扩展。要注意的是Redis扣减成功后库存数据最终要异步同步回数据库作为最终的财务和库存对账依据。同步过程中用MQ或者定时任务双写允许数据库库存有一定延迟但绝对不能拿Redis的库存数据直接当作最终的财务数据。支付成功后才会正式扣减数据库库存下单只是预占。这个异步对账链路是每次大促之后我都要重点盯的。3.2 分布式事务与最终一致性把强一致让位给吞吐下单链路涉及订单、库存、优惠券、用户余额等多个系统的数据变更。如果每个操作都用分布式事务框架如Seata的AT模式做强一致提交由于全局锁和大量网络交互吞吐会急剧下降晚高峰根本扛不住。我的方案是核心链路本地事务非核心链路最终一致性。用户点击下单提交后在订单服务本地事务里先创建订单状态为待支付同时发送一条库存预占消息到MQ。库存服务消费消息执行库存预占。如果库存预占失败订单服务的事务已经提交了这时候通过消息中的订单ID和状态机将订单状态流转为已取消。用户看到的反馈是下单失败或者库存不足业务上可以接受。这里要强调的是对账补偿机制。下游消息消费失败或者超时不能依赖肉眼发现。要有对账Job定时扫描待支付超过一定时间的订单和库存预占状态不一致的记录自动补单、自动释放或者人工介入。晚高峰期间核心数据的一致性风险不是靠架构完全消灭的而是通过数据比对和补偿机制兜底。分布式事务的选型上如果业务确实需要强一致比如支付场景我更推荐事务消息方案RocketMQ的事务消息而不是两阶段提交。事务消息先把消息发送到MQ的half状态执行本地事务本地事务提交后再确认消息为可消费状态。本地事务回滚则取消消息。这个方案相比Seata实现简单、性能损失小业务侵入也低。3.3 幂等设计回调重试、重复下单和补偿对账高并发系统里网络抖动和重试是常态。如果接口不做好幂等重复请求会造成重复下单、重复扣款、重复发放优惠券这类事故在晚高峰和大促中特别常见。订单创建接口的幂等一般在请求头或者请求体里带上一个客户端生成的幂等键比如用户的设备号加时间戳生成的唯一ID订单服务在创建前先去Redis查一下幂等键是否已存在存在则直接返回已创建的订单不存在则加锁写订单。数据库层面订单号本身有唯一索引重复插入会报冲突服务捕获这个异常后返回原订单信息而不是报错给用户。支付回调是最容易出问题的地方。支付平台回调系统的通知机制有最多24小时224次通知这种策略而且回调顺序和重复次数不可控。支付回调接口的幂等处理几乎都是拿支付平台的交易单号作为唯一索引先查后插插入冲突则按已处理逻辑返回成功。这里有一个我踩过的坑唯一索引和先查后插在并发下会有风险因为先查这一步可能两个请求同时查到不存在然后都去插入虽然唯一索引能拦住一个但另一个会报错。正确做法是直接用insert ignore或者insert on duplicate key update让数据库帮你完成幂等。补偿对账系统的幂等同样重要。对账任务扫描出的数据差异处理时也要有唯一任务ID数据库加唯一约束防止两个任务同时处理同一笔异常数据。4. 读链路与热点治理详情页、Feed流和高频数据的三级缓存读流量占晚高峰总流量的80%以上处理好读链路系统的压力就消除了一大半。读链路的治理核心不是加缓存这么简单而是把缓存分成三级每一级对应不同的数据特征和访问频度。4.1 静态化与CDN把不动的数据推到离用户最近的地方电商平台上静态资源非常多商品主图、品牌Logo、营销活动页面模板、楼层组件、商品详情页的框架CSS和JS。这些资源有一个特点——内容不经常变化至少对绝大多数用户在绝大多数时间内是一样的。把这些资源交给CDN是最便宜、最高效的流量卸载方式。CDN的价值不只是缓存命中。它把你的资源分发到离用户最近的边缘节点用户从CDN节点拿资源不经过自己的源站源站的带宽和并发压力都大幅下降。我在大促压测里测过全站静态资源命中CDN后源站的请求量可以下降60%到70%。商品详情页的静态化要做得更极致。爆款商品的详情页在后台商品编辑完成后直接生成静态HTML页面推送到CDN。静态页面里商品价格、图片、详情文案都是固定的用户请求直接命中CDN边缘节点全程不经过应用服务器。价格、库存这类变化频繁的信息通过页面异步接口动态加载不回源到应用服务器而是回源到Redis缓存。这样既保证了详情页大部分内容的极速展示又不至于所有数据都静态化导致无法更新。活动页的更细做法是静态页模板加数据接口模板托管在CDN活动数据比如秒杀价格、参与商品通过接口异步获取。这个模式下活动页的访问量再大冲击的也主要是CDN节点和少量数据接口而不是整个应用集群。4.2 多级缓存与热点Key探测本地缓存扛住流量尖峰动态数据的读缓存最有效的组合是本地缓存CaffeineRedis分布式缓存数据库三级。本地缓存放在JVM进程内速度最快纳秒级容量小适合高频访问且允许短时间不一致的数据Redis缓存是分布式共享的适合一致性要求更高或者数据量更大的场景。晚高峰的流量尖峰落到一个具体业务场景往往是首页Feed流每秒钟几万次请求同一个商品ID。如果所有请求都打到Redis即使Redis扛得住网络IO和序列化反序列化的开销也很可观。更聪明的做法是在本地缓存里再放一层比如Caffeine配置expireAfterWrite为几秒。同一个服务实例上几万个请求只有第一个请求穿透到Redis其余的直接从本地内存返回服务实例的CPU消耗显著下降。热点Key的探测要主动做。被动等监控告警再去解决流量尖峰早就过去了。我的做法是在Redis客户端或者网关层做访问频次的滑动窗口统计当某个Key在一段时间内的访问QPS超过预设阈值比如5000就把这个Key识别为热点Key热点key信息推送到各个服务实例服务实例把这部分数据放长一点时间的本地缓存。这个方案需要额外的上报和推送通道但收益非常直接——热点Key不再只打在一个Redis节点上而是被分散到所有服务实例的本地缓存里。需要注意的热点Key治理是个治理问题不是一次配置就结束的。每次大促前我会拉取前几次的流量日志做热点Key分析建立热点Key清单对这些Key单独设置本地缓存、单独设置Redis过期时间。还有一些动态热点比如某个直播间突然引爆一个商品这时候靠运维手工加缓存往往来不及需要Client侧的探测和推送机制自动完成。4.3 限流、熔断与降级用优先级矩阵保住核心交易不管架构做得多好真实流量总是会超出预期。这时候不是让所有请求都通过然后全部失败而是有选择地牺牲一部分非核心请求保住核心交易链路。限流先从接入层开始。Nginx层我用过两种算法漏桶算法适合限制突发流量让请求以恒定速率通过保护后端服务令牌桶算法允许一定的突发流量适合业务上可以接受短时间内请求量略有超标的场景。网关层做更精细的限流按接口维度、用户维度、来源维度分别限制。比如每个普通用户每秒只能请求一次下单接口同一IP每秒最多请求5次详情页接口。熔断是保护依赖的。下游服务或者Redis/数据库等依赖出现大量超时或错误时熔断器打开上游直接返回降级结果不再等待下游超时。熔断状态有关闭、打开、半开三种。半开状态允许少量请求试探下游是否恢复试探成功则关闭熔断试探失败则继续打开。熔断阈值通常设置错误率达到50%以上时触发探测间隔建议15到30秒。降级的核心是想清楚优先级。我维护过一张电商系统的降级优先级矩阵第一优先级是下单、支付、退款这些资金链路绝对不能降级第二优先级是商品查询和购物车可以降级为返回缓存数据但不影响用户下单第三优先级是评论、收藏、推荐、物流查询这些体验类功能全站压力过大时直接关闭用户看到功能暂时不可用。降级的执行要自动化通过配置中心推送开关不是靠运维手工改代码否则人根本来不及反应。5. 全链路压测与容量评估上线前要把系统打到极限架构设计得再好不上压测验证都是纸上谈兵。晚高峰几十万并发的系统上线前必须有完整容量评估和全链路压测的数据支撑扩容几台机器、加多少缓存容量都要有根据。5.1 容量评估的QPS模型从转化率倒推每个环节的请求量容量评估不是拍脑袋而是从业务目标倒推。预测晚高峰的峰值订单量是10万单/小时多长的时间窗内完成假设集中在15分钟内平均每秒大约111单。但这个平均QPS没有参考价值还要乘一个突发系数晚高峰场景一般取3到5倍那么下单QPS峰值大约333到555我再预留50%的余量下单QPS按800设计。下单链路只是冰山一角。从下单QPS反推用户每下一单背后要看多少次详情页行业经验值大概是10到20次我做过统计爆款商品的详情页浏览次数与下单量比值可能超过30。那么详情页QPS 800 × 15 12000。搜索页和列表页的比例也在10倍以上这两种页面的聚合逻辑更重对缓存的压力更大。购物车、地址管理、优惠券计算这些支撑接口通常各占详情页流量的20%到30%。确定每个接口的QPS目标后做容量计算。比如订单创建服务单实例在压力测试下能支撑200QPS需要800的QPS就要至少4个实例考虑单实例故障的情况再乘1.5的冗余系数部署6到8个实例比较稳妥。Redis集群的容量评估要看数据量和QPS的双重维度数据量预估当前全量数据加7天内的增量再乘2倍的冗余QPS按总请求量的80%穿透到Redis计算再除以集群节点数确保单节点QPS不超过5万留有余量。5.2 压测准备与工具选型预热、数据隔离和监控全链路压测最怕的不是压出问题是压测数据污染真实数据。必须做完整的压测隔离。流量隔离方面压测请求通过Header标记如X-Pressure-Test: true网关识别后把压测流量转发到压测的独立集群或者压测环境。数据隔离方面压测产生的订单、支付流水、库存扣减记录要打上压测标记写进独立的影子表或者其他环境的分库中绝不能和真实数据混在一起。我在第一次全链路压测时没做严格隔离压测订单混入真实订单池第二天财务对账差点出大事这个坑印象非常深刻。压测工具选型上接口级压测我用wrk或者Apache JMeter。wrk基于epoll单机可以产生很高的并发连接适合快速验证单个接口的QPS上限和延迟曲线JMeter适合复杂场景的组合脚本多步业务流程可以用JMeter的Thread Group和逻辑控制器编排。大规模长时间压测用Cloud Native的压测平台或者分布式压测工具K6可以在云上开多台施压机模拟百万级别的并发。无论用哪种工具关键都要关注延迟分布尤其是P99延迟而不是只看平均延迟。平均延迟被少量慢请求拉高的场景下P99才能真实反映用户体验。压测过程中的监控要全面。对应用层看CPU、内存、GC、线程池活跃线程数、Tomcat连接数中间件看Redis的命中率和慢查询、MQ的积压量和消费速率、数据库的连接数、慢SQL和主从延迟基础设施看带宽和负载。这些指标要落在统一监控平台上压测过程中实时刷新任何一项达到阈值立刻标记为瓶颈项。5.3 全链路压测的三个常见误区压测过程中我总结了三个容易犯的误区每一个都真实踩过。误区一不预热就压测。JVM的即时编译需要时间才能达到峰值性能Redis和数据库的缓存也需要预热才能模拟真实命中率。如果直接上压测流量第一轮的结果根本不能代表系统真实能力。正确做法是先跑一段时间低流量预热让JIT编译完成、缓存填满再逐步加压。误区二压测目标只盯QPS不管响应时间和成功率。接口QPS上去了但P99延迟从200ms涨到3秒成功率掉到99.5%这实际上是不过关的。晚高峰场景要守住的是成功率不低于99.9%、P99延迟在500ms以内这一类的SLAQPS只是其中一个参数。误区三只做接口压测不做全链路压测。单接口压测时每个接口都能扛住但全链路压测时下单接口和支付回调同时达到峰值数据库连接池和线程池的竞争会把整个系统拖垮。只有全链路压测才能暴露服务之间的资源竞争和级联故障。大促前我至少要跑三次全链路压测第一次摸底第二次过容量规划第三次做故障演练用混沌工程的方式随机杀掉节点观察系统的自愈能力。6. 晚高峰故障复盘真实场景下的连环坑与应对清单做高并发系统设计最后拼的不是方案先进而是应对故障的经验沉淀。这里记录几个我亲历的晚高峰故障场景复盘完整链路给各位参考。6.1 缓存击穿引发数据库打挂的完整复盘链路某一年的晚高峰详情页接口P99延迟突然从300ms飙升到8秒紧接着数据库连接数告警。整个过程看起来是数据库被打挂了但根因是缓存击穿。复盘的完整链路是这样的某个爆款商品的缓存Key在晚上八点整过期这个商品正好是直播间主推款请求量极大。缓存过期的一瞬间大量请求同时发现Redis没有数据全部穿透到数据库查询。数据库一个普通主键查询也扛不住几千QPS的并发连接池被打满数据库CPU飙升到100%所有依赖这个数据库的接口全部超时进而导致服务线程池被打满整个详情页服务不可用。这个坑的根因是缓存Key过期时间设置不当所有用户同时触发缓存重建。解决方案有几种我后来都加上了一是热点Key设置永不过期由后台任务异步更新缓存保证缓存永远存在只是数据可能短暂不新二是互斥锁同一时间只允许一个请求回源数据库其他请求短暂等待后从缓存读取三是加JVM本地缓存兜底即使Redis缓存失效本地缓存还能撑一会儿给异步重建留出时间。事后我养成了一个习惯每天晚上八点前后我都会盯一眼核心缓存Key的过期时间分布绝对不允许热点缓存集体过期。这是用一次晚高峰事故换来的教训。6.2 库存热分片倾斜数据分布比机器性能更容易成为瓶颈另一场晚高峰事故是库存服务Redis Cluster的一个节点CPU被打满但集群其他节点CPU使用率都低于30%。排查后发现爆款商品的库存Key通过Hash函数路由到了同一个Hash Slot这个槽位正好落在了一个Redis节点上晚高峰所有扣减库存的请求都落在这个节点上单节点的CPU和带宽都成了瓶颈。数据倾斜这个问题的隐蔽性在于监控系统里平均负载看起来很正常只有按节点维度看才能发现是一个人累死其他人围观。解决思路有几个方向一是热点Key的拆散把一个商品库存Key拆成多个子Key比如sku_stock_1、sku_stock_2请求通过随机数分散到不同子Key扣减时用Lua脚本把这几个子Key的库存加起来判断二是热点Key单独设集群或者单独的Redis实例三是在业务上做本地化处理把热点商品的库存预占逻辑前置到多级缓存减少对Redis单一节点的冲击。我实践下来子Key拆散是最有效的通用方案但要注意拆散后的库存一致性判断逻辑复杂度会增加必须仔细测试。6.3 晚高峰作战手册我每天必盯的指标和红线经历过几次事故后我把晚高峰要盯的指标整理成了一份清单上线前给团队每个人发一份每天八点上线前逐项检查。核心指标清单交易链路的成功率低于99.9%解锁处理下单接口的P99延迟超过800ms要排查订单服务的线程池活跃比超过70%要扩容数据库连接数使用率超过60%要优化连接池或者限流Redis命中率低于85%要检查缓存策略MQ消费积压数超过100万必须介入四层负载和Nginx的带宽使用率超过70%要考虑升级带宽。我自己的习惯是准备一个作战大屏把以上指标和告警集中在一张屏幕上。晚高峰期间团队只需要盯大屏上的红色和黄色状态不轻易去查分散的监控系统——分散的信息和告警风暴才是乱局的根源。另外一套动作是晚高峰开始的五分钟内主动做一次服务降级检查确认降级开关的状态是符合预期的核心交易链路的熔断阈值没有调错。很多事故不是系统扛不住是前一天有人调整了熔断阈值或者限流策略晚高峰一到就误伤。这个检查动作虽然简单但成本极低收益极大我强烈建议作为固定流程。结尾做高并发架构这些年我最深的体会是架构不是一锤子买卖而是一套持续演进的系统工程。每一次大促、每一个晚高峰结束后的复盘都会发现新的薄弱点可能是缓存策略不够精细可能是某个中间件的参数没调对也可能是某个接口的幂等逻辑有漏洞。把每一次压测和故障当成打磨系统的机会高并发能力就是这样一点点积累起来的。最后再分享一个小技巧每次晚高峰临近结束的十五分钟也就是晚上九点四十五左右流量开始回落的时候把核心指标截图保存下来和前一天同时段的指标做个对比。这种同环比的习惯能帮你提前发现那些还没有完全爆发的隐患。系统设计能力的提升往往就藏在这些每天重复做的细节动作里。
返回列表