如何设计亿级流量的高并发系统
2026-09-23 14:08:15 0 举报一说高并发就只会"加机器"?面试被问"设计个秒杀系统"直接懵圈? 这份脑图把亿级流量的底层逻辑给你盘明白了。 从缓存防雪崩、MQ削峰,到分库分表、限流熔断,再到硬核实战"秒杀系统",每步都有落地方案。 只讲怎么扛流量、防崩溃。不管是应对面试还是真刀真枪扛业务,捋一遍这张图,让你面对百万QPS也能稳如老狗!
高并发架构
秒杀系统设计
分库分表
缓存雪崩
架构师
模板推荐
作者其他创作
大纲/内容
📐 一、认知篇:建立高并发的"度量衡"
📖 1.1 什么是高并发系统
定义:系统在【大量请求同时到达】时,仍能保持【高性能】和【高可用】的能力
高并发 ≠ 高性能 ≠ 高可用(三个不同的概念,但相关)
高性能:单个请求处理得快
高并发:能同时处理很多请求
高可用:系统持续可用,不宕机
高并发系统的典型特征
海量用户同时访问
请求量大、峰值高
对响应时间敏感
对可用性要求极高
📊 1.2 核心指标体系(架构师必须量化)
QPS / TPS(每秒查询数/每秒事务数)
QPS:每秒处理的请求数(衡量吞吐能力)
TPS:每秒完成的事务数(衡量业务处理能力)
关键区分:峰值 QPS vs 平均 QPS(设计按峰值)
响应时间(RT, Response Time)
用户感知的核心指标
关注点:不仅看平均值,更要看 P99/P999(长尾)
例:平均 50ms,但 P99 是 2s,说明有 1% 用户体验极差
吞吐量(Throughput)
单位时间内系统处理的总数据量/请求量
与 QPS 相关,但更强调"总量"
并发数(Concurrency)
同一时刻系统正在处理的请求数
并发数 ≠ QPS(并发是"同时在处理",QPS 是"每秒处理完")
关系:QPS ≈ 并发数 / 平均响应时间(利特尔法则)
可用性(Availability)
用"几个9"衡量:99.9%(3个9)、99.99%(4个9)
99.9% = 一年约 8.7 小时不可用
99.99% = 一年约 52 分钟不可用
99.999% = 一年约 5 分钟不可用
核心认知:每多一个9,成本指数级上升
🧠 1.3 高并发设计的五大核心哲学(贯穿全文的思想)
哲学1:分而治之(Divide and Conquer)
思想:把大问题拆成小问题,把大流量分散到多个节点
体现:负载均衡、分库分表、微服务拆分、CDN 分发
本质:用"并行"对抗"海量"
哲学2:空间换时间(Space for Time)
思想:用额外的存储空间,换取更快的访问速度
体现:缓存(把计算结果/数据存起来,避免重复计算/查询)
本质:存储便宜,计算和等待贵
哲学3:异步化(Asynchronization)
思想:把"必须同步等待"的操作改为"异步处理",提升响应速度和吞吐量
体现:消息队列、异步线程池、非阻塞 IO
本质:不让用户等待非核心操作
哲学4:池化(Pooling)
思想:预先创建并复用资源,避免频繁创建/销毁的开销
体现:数据库连接池、线程池、对象池
本质:复用昂贵资源
哲学5:冗余(Redundancy)
思想:通过备份/多副本来避免单点故障,提升可用性
体现:集群部署、主从复制、多机房部署
本质:用成本换可用性
⚠️ 1.4 高并发的三大挑战(设计时必须面对)
挑战1:性能问题
单机处理能力有限,如何提升整体处理能力?
核心手段:缓存、异步、并发、优化
挑战2:可用性问题
任何节点都可能故障,如何保证系统整体可用?
核心手段:冗余、容错、降级、限流
挑战3:数据一致性问题
分布式环境下,如何保证数据的正确性?
核心手段:事务、最终一致性、补偿
三者关系:往往需要权衡(如强一致性会牺牲性能,高可用会增加成本)
🗺️ 二、全局视角:高并发系统的分层架构
🏗️ 2.1 整体架构图景(一个请求的完整旅程)
文字描绘完整链路
用户发起请求
→ DNS 解析(找到最近的接入点)
→ CDN(静态资源直接返回,不到源站)
→ 负载均衡(将流量分发到多台服务器)
→ 网关(鉴权、限流、路由)
→ 应用服务集群(处理业务逻辑)
→ 缓存层(优先从缓存读,减少数据库压力)
→ 数据库(最终的数据存储)
→ 原路返回给用户
关键认知:每一层都是一道"防线",层层过滤,越往后流量越少
🎯 2.2 每一层的职责与优化空间
接入层(DNS/CDN/负载均衡)
职责:流量接入、就近分发、静态资源加速
优化目标:让请求"少跑远路",静态请求"不源头"
网关层
职责:统一入口、鉴权、限流、路由
优化目标:拦截非法/超量请求,保护后端
应用层
职责:业务逻辑处理
优化目标:无状态化、水平扩展、异步化
缓存层
职责:加速读、减少数据库压力
优化目标:提高命中率、保证一致性
存储层(数据库)
职责:数据持久化
优化目标:读写分离、分库分表、优化SQL
异步层(消息队列)
职责:解耦、削峰、异步处理
优化目标:可靠投递、顺序、幂等
🪣 2.3 木桶理论:瓶颈决定整体性能
核心认知:系统的整体性能取决于【最薄弱的一环】
例:应用层优化得再好,数据库是瓶颈,整体性能仍上不去
架构师的职责:找到瓶颈,优先优化瓶颈
找瓶颈的方法:压测 + 监控 + 链路追踪
关键原则:优化要"对症下药",不要盲目优化非瓶颈
🎯 2.4 高并发设计的分层防御思想
思想:像"多层过滤器"一样,层层削减流量
CDN 挡掉 80% 的静态请求
负载均衡分散到多台机器
网关限流挡掉超量请求
缓存挡掉 90% 的读请求
最终到数据库的流量可能只有原始的 1%
价值:每一层都在"保护"下一层,避免瓶颈被打穿
架构师视角:设计时要考虑"每一层的过滤比例"
🌐 三、接入层设计:流量进入系统的第一道关
📖 3.1 为什么接入层很重要
接入层是用户请求的第一站
好的接入层设计能大幅减轻后端压力
核心目标:让请求"就近接入、快速响应、合理分发"
🌍 3.2 DNS 优化
DNS 的作用:将域名解析为 IP 地址
DNS 负载均衡
原理:一个域名配置多个 IP,DNS 返回不同 IP 实现分流
优点:简单、在解析层就分流
缺点:DNS 有缓存,更新不灵活;无法根据负载动态调整
智能 DNS / 就近解析
根据用户的地理位置,返回最近的服务器 IP
例:北京用户解析到北京机房,广州用户解析到广州机房
价值:减少网络延迟,提升用户体验
DNS 的局限
只能做粗粒度的流量分发
不适合精细的负载均衡
🚀 3.3 CDN 加速(内容分发网络)
CDN 解决什么问题
用户离服务器太远,访问慢
静态资源(图片、视频、JS、CSS)占用大量带宽
CDN 原理
在全国/全球部署边缘节点
将静态资源缓存到离用户最近的边缘节点
用户请求时,从最近的边缘节点获取,不回源
CDN 能挡掉什么
静态资源请求(图片、视频、静态页面)
这些请求占比往往高达 70%-90%
核心认知:CDN 是高并发系统"性价比最高"的优化
CDN 的适用与局限
适用:静态资源、读多写少、内容分发
局限:动态内容(个性化)难以缓存,需回源
动态加速
对于无法缓存的动态请求,CDN 提供"动态加速"(优化路由、专线)
⚖️ 3.4 负载均衡(流量分发)
负载均衡解决什么问题
单台服务器扛不住,需要多台服务器分担
如何将请求合理分发到多台服务器?
负载均衡的分类
硬件负载均衡:F5(专用设备,性能强,贵)
软件负载均衡:Nginx、LVS、HAProxy(灵活,便宜)
云负载均衡:云厂商提供的(如阿里云 SLB)
负载均衡的层次
四层负载均衡(L4):基于 IP + 端口(如 LVS)
七层负载均衡(L7):基于应用层内容,如 URL、Header(如 Nginx)
区别:七层更灵活(可按 URL 路由),四层性能更高
常见调度算法
轮询(Round Robin):依次分发,简单
加权轮询:按权重分发(性能好的多分)
最少连接:分给当前连接数最少的服务器
IP 哈希:同一 IP 固定分发到同一台(用于会话保持)
一致性哈希:用于缓存场景,减少节点变动的影响
负载均衡的高可用
负载均衡器本身也可能成为单点
解决:负载均衡器也要做集群(主备/多活)
🚪 四、网关层设计:流量的统一入口
📖 4.1 为什么需要网关
后端有大量微服务,需要一个统一入口
网关承担"门卫"职责:所有请求先经过网关
价值:统一管理、保护后端、简化客户端
🛠️ 4.2 网关的核心职责
路由:根据请求路径,转发到对应的后端服务
鉴权:验证用户身份(Token、登录态)
限流:控制流量,保护后端(详见限流)
熔断:后端故障时快速失败
日志与监控:统一记录请求日志
协议转换:如将外部 HTTP 转为内部 RPC
🚦 4.3 限流的四种算法(重点,面试高频)
计数器算法(固定窗口)
原理:在固定时间窗口内计数,超过阈值则拒绝
优点:简单
缺点:临界问题(窗口边界可能瞬间通过2倍流量)
滑动窗口算法
原理:将时间窗口细分成多个小格子,滑动统计
优点:解决临界问题,更平滑
缺点:实现稍复杂
漏桶算法(Leaky Bucket)
原理:请求进入"桶",桶以固定速率"漏水"(处理请求)
优点:流量绝对平滑,保护下游
缺点:无法应对突发流量(即使系统有余力,也匀速处理)
令牌桶算法(Token Bucket)
原理:桶里放令牌,请求需拿到令牌才能通过;令牌以固定速率生成
优点:允许一定程度的突发流量(桶里攒了令牌可以一次用)
缺点:实现相对复杂
实际应用:最常用,如 Guava RateLimiter
算法选型建议
需要绝对平滑:漏桶
允许突发:令牌桶(推荐)
简单场景:计数器/滑动窗口
🏗️ 4.4 网关的高可用设计
网关是所有流量的必经之路,必须高可用
手段:网关集群部署(无状态,可水平扩展)
避免网关成为单点
🧰 4.5 常见网关技术选型
Nginx:高性能,适合做接入层/反向代理
Spring Cloud Gateway:Java 生态,适合微服务网关
Kong / APISIX:功能丰富的 API 网关
选型考虑:性能、功能、团队技术栈、扩展性
🖥️ 五、应用层设计:业务逻辑的高并发承载
📖 5.1 应用层在高并发中的角色
应用层承载核心业务逻辑
高并发的关键:应用层要能"水平扩展",加机器就能扛更多流量
核心原则:应用必须【无状态】
🔄 5.2 无状态设计(水平扩展的前提)
什么是有状态
应用实例内存中保存了用户相关数据(如 Session)
问题:用户下次请求必须打到同一台实例,无法随意分发
什么是无状态
应用实例不保存用户相关数据
用户状态外置(存到 Redis、数据库)
好处:任何请求可以打到任何实例,可随意水平扩展
无状态化的手段
Session 集中存储:用 Redis 存 Session
Token 方案:用 JWT 等 Token,状态在 Token 里
本地缓存慎用:会破坏无状态(或需考虑一致性)
核心认知:无状态是水平扩展的"入场券"
📈 5.3 水平扩展与垂直扩展
垂直扩展(Scale Up)
增强单机性能(加 CPU、加内存)
优点:简单,不改架构
缺点:有物理上限、成本高、单点风险
水平扩展(Scale Out)
增加机器数量,分担流量
优点:理论上无上限、成本可控
缺点:需要应用无状态、需要负载均衡、分布式复杂性
架构师建议
优先水平扩展(符合高并发需求)
垂直扩展作为辅助(优化单机)
🏊 5.4 连接池技术(池化思想的体现)
为什么需要连接池
数据库连接、HTTP 连接的创建/销毁开销大
高并发下频繁创建连接会拖垮系统
连接池原理
预先创建一批连接,放入池中
需要时从池中"借",用完"归还"
避免重复创建/销毁
常见连接池
数据库连接池:HikariCP、Druid
HTTP 连接池:HttpClient、OkHttp
连接池调优要点
池大小:太小不够用(等待),太大浪费资源(连接数过多压垮数据库)
关键认知:连接池不是越大越好,要与下游承受能力匹配
🧵 5.5 线程模型与异步化
同步阻塞的问题
一个请求占用一个线程,线程等待时啥也干不了
高并发下线程数爆炸,上下文切换开销大
异步化的价值
不等待非核心操作,快速响应用户
例:下单后,发短信、记积分等操作异步处理
线程池的使用
用线程池管理线程,避免频繁创建
注意线程池参数调优(核心线程数、队列、拒绝策略)
响应式/非阻塞编程
如 WebFlux、Reactor
用少量线程处理大量并发
📊 5.6 应用的容量评估
单实例能扛多少 QPS?(通过压测得出)
需要多少实例?(目标 QPS / 单实例 QPS,再留余量)
核心认知:容量评估要基于压测数据,而非拍脑袋
⚡ 六、缓存设计:高并发的"第一利器" ★核心
📖 6.1 为什么缓存是高并发的第一利器
数据库的读性能有限(单机几千 QPS)
而读请求往往占 80%-90%
缓存将热点数据放在内存中,读性能提升数十倍到数百倍
核心思想:空间换时间 + 挡住读流量,保护数据库
🗂️ 6.2 缓存的分类
按位置分
本地缓存:存在应用进程内存中(如 Caffeine、Guava Cache)
优点:极快(无网络开销)
缺点:容量有限、多实例不一致、重启丢失
分布式缓存:独立部署的缓存服务(如 Redis、Memcached)
优点:容量大、多实例共享、高可用
缺点:有网络开销
按用途分
数据库缓存:缓存数据库查询结果
页面缓存:缓存整个页面(如 CDN、页面静态化)
会话缓存:缓存 Session/Token
📚 6.3 缓存读写策略
Cache Aside(旁路缓存,最常用)
读:先读缓存 → 缓存没有则读数据库 → 写入缓存
写:先更新数据库 → 再删除缓存(而非更新缓存)
为什么写时删缓存而不是更新缓存:避免并发写导致的不一致
Read/Write Through(读写穿透)
缓存作为数据访问的统一入口
缓存未命中时,缓存层自动从数据库加载
写时,缓存层自动同步到数据库
Write Behind(异步写回)
写时只写缓存,异步批量刷回数据库
优点:写性能极高
缺点:缓存挂了可能丢数据
策略选型
读多写少:Cache Aside
写密集且可容忍丢失:Write Behind
🕳️ 6.4 缓存三大问题及解决方案(重点,必考)
缓存穿透(查不存在的数据)
问题:查询一个数据库中【根本不存在】的数据
现象:缓存没有 → 查数据库 → 数据库也没有 → 无法写缓存 → 每次都打到数据库
恶意场景:黑客用大量不存在的 ID 攻击,打垮数据库
解决方案
方案1:缓存空值(查不到也缓存一个空值,设短过期时间)
方案2:布隆过滤器(Bloom Filter):预先判断数据是否存在,不存在直接拦截
缓存击穿(热点 key 过期)
问题:某个【热点 key】突然过期
现象:大量并发请求同时发现缓存失效,同时打到数据库
与穿透的区别:击穿是"热点数据过期",穿透是"数据根本不存在"
解决方案
方案1:互斥锁(只让一个线程去重建缓存,其他等待)
方案2:热点 key 永不过期(逻辑过期,后台异步更新)
方案3:过期时间加随机值(避免大量 key 同时过期)
缓存雪崩(大量 key 同时失效)
问题:大量缓存【同时过期】,或缓存服务宕机
现象:大量请求同时打到数据库,数据库被压垮,可能引发系统雪崩
与击穿的区别:击穿是"单个热点 key",雪崩是"大量 key"
解决方案
针对同时过期:过期时间加随机值,打散过期时间
针对缓存宕机:缓存高可用(集群)、多级缓存、限流降级兜底
🔄 6.5 缓存一致性问题(难点)
问题本质:缓存和数据库是两份数据,如何保持一致?
不一致的场景
先更新数据库,再删缓存,但删缓存失败
并发读写导致的时序问题
常见解决方案
方案1:更新数据库 + 删除缓存(Cache Aside)
最常用,能接受短暂不一致
方案2:延迟双删
更新数据库 → 删缓存 → 延迟一段时间 → 再删一次缓存
解决并发读的脏数据问题
方案3:基于 Binlog 的异步同步
通过监听数据库 Binlog(如 Canal),异步删除/更新缓存
优点:业务代码无侵入,可靠性高
方案4:设置较短的过期时间
即使不一致,也会在过期后自动修复(最终一致性)
架构师视角
强一致性很难且成本高,大多数场景接受"最终一致性"
根据业务容忍度选择方案
🏗️ 6.6 多级缓存架构
思想:缓存不止一层,层层递进
完整链路
浏览器缓存(本地)
→ CDN(边缘节点)
→ Nginx 本地缓存
→ 应用本地缓存(Caffeine)
→ 分布式缓存(Redis)
→ 数据库
价值:层层过滤,越靠前的缓存命中率越高,对后端的压力越小
注意:多级缓存的一致性问题更复杂,需谨慎设计
🛡️ 6.7 Redis 的高可用(支撑缓存层的可用性)
主从复制(Replication)
一主多从,主写从读,读写分离
问题:主挂了需要手动切换
哨兵模式(Sentinel)
在主从基础上,增加哨兵节点监控主节点
主挂了,哨兵自动将从节点提升为主
实现自动故障转移
Cluster 集群模式
Redis 官方集群方案,数据分片存储在多个节点
支持水平扩展,解决单机容量和性能瓶颈
生产环境推荐
选型建议
小规模:主从 + 哨兵
大规模/高并发:Cluster
📨 七、消息队列与异步化:削峰填谷的核心 ★核心
📖 7.1 为什么需要异步化
同步调用的问题
用户下单,要同步执行:扣库存、扣款、发短信、记积分……
所有操作串行执行,响应时间 = 所有操作之和
用户体验差,且一个环节失败影响整个流程
异步化的思想
核心操作同步执行(扣库存、扣款)
非核心操作异步执行(发短信、记积分、发通知)
用户快速得到响应,后台慢慢处理非核心操作
价值:提升响应速度、提升吞吐量、解耦
🎯 7.2 消息队列的三大作用
作用1:异步
把耗时操作异步处理,快速响应用户
例:下单后异步发短信
作用2:削峰
突发大量请求时,MQ 作为"缓冲区"
请求先写入 MQ,消费者按自己的能力慢慢消费
保护下游系统不被瞬时流量打垮
例:秒杀时,订单请求先入 MQ,慢慢创建订单
作用3:解耦
上游系统只需发消息,不关心谁消费
下游系统各自订阅消息
例:订单系统发消息,积分系统、物流系统、通知系统各自消费
价值:新增下游无需改上游代码
⚖️ 7.3 主流 MQ 对比(选型)
Kafka
特点:高吞吐、适合大数据/日志场景
优点:吞吐量极高、水平扩展能力强
缺点:功能相对简单,不适合复杂业务消息
适用:日志采集、大数据流、高吞吐场景
RocketMQ
特点:阿里开源,功能丰富
优点:支持事务消息、延迟消息、顺序消息,适合金融级业务
缺点:吞吐量低于 Kafka
适用:电商、金融等对可靠性要求高的业务场景
RabbitMQ
特点:基于 Erlang,功能完善,支持 AMQP
优点:功能丰富、路由灵活、生态成熟
缺点:吞吐量较低
适用:中小规模、对路由灵活性要求高的场景
选型建议
大数据/日志:Kafka
电商/金融业务:RocketMQ
中小项目/灵活路由:RabbitMQ
🔒 7.4 消息的可靠性保证(不丢失)
消息丢失的三个环节
生产端丢失:生产者发送失败
MQ 端丢失:MQ 收到后宕机,未持久化
消费端丢失:消费者收到后处理失败
对应的解决方案
生产端:确认机制(发送后等 MQ 确认)、重试
MQ 端:消息持久化(写入磁盘)、多副本
消费端:手动确认(处理成功才 ACK)、失败重投
核心认知:可靠性 = 生产确认 + 持久化 + 消费确认
🔁 7.5 消息幂等性(不重复消费)
为什么会有重复消息
网络重试、消费者重启后重新消费
MQ 的"至少一次投递"语义
重复消费的危害
例:重复扣款、重复发短信
解决方案
唯一消息 ID + 去重表/Redis
业务幂等设计(如状态机,已处理过的不再处理)
核心认知:消息可能重复,消费必须幂等
📋 7.6 消息顺序性
为什么需要顺序
例:订单的"创建→支付→发货"必须按序处理
问题:MQ 默认不保证全局顺序
解决方案
全局顺序:性能差,很少用
分区顺序:将同一业务的消息发到同一个分区/队列(如按订单ID哈希)
同一订单的消息在一个分区内有序
核心认知:用"局部有序"替代"全局有序",兼顾性能和顺序
🗄️ 八、数据库设计:最终的瓶颈与攻坚 ★核心
📖 8.1 为什么数据库是最终瓶颈
所有数据最终要落库
缓存只能挡读,写操作必须到数据库
数据库的写入性能、连接数、存储容量都有上限
核心认知:数据库是高并发系统的"最后一道防线",也是最难扩展的
📊 8.2 数据库的瓶颈分析
连接数瓶颈:数据库连接数有限,高并发下连接不够用
CPU 瓶颈:复杂查询、大量写入消耗 CPU
IO 瓶颈:磁盘读写速度慢(尤其机械硬盘)
锁瓶颈:并发写入时的锁竞争
容量瓶颈:单表数据量过大(超过千万行性能下降)
📖 8.3 读写分离(第一层优化)
原理
主库(Master)负责写
从库(Slave)负责读
主库将数据同步到从库(主从复制)
价值:读写分离,读流量分散到多个从库
主从复制原理
主库将写操作记录到 Binlog
从库拉取 Binlog 并重放,实现数据同步
读写分离的问题:主从延迟
问题:主库刚写入,从库还没同步,读从库读到旧数据
解决:
对一致性要求高的读,走主库
能容忍短暂延迟的读,走从库
关键操作后强制读主库
🔀 8.4 分库分表(第二层优化,核心)
为什么需要分库分表
单表数据量太大(千万级以上),查询变慢
单库写入性能不足
单库存储容量不足
垂直拆分
垂直分库:按业务拆分(用户库、订单库、商品库)
依据:业务边界,不同业务的数据分开
垂直分表:把一张大表按列拆分(把不常用的列拆出去)
依据:字段的使用频率、大小
水平拆分
水平分库:同一张表的数据分散到多个库
水平分表:同一张表的数据分散到多张表
依据:按某个规则(如用户ID)将数据打散
垂直 vs 水平
垂直:按"业务/列"拆,解决业务耦合和大字段问题
水平:按"行"拆,解决单表数据量过大问题
实际中往往结合使用
🎯 8.5 分片策略与分片键选择(重点难点)
什么是分片键(Sharding Key)
决定数据落到哪个库/表的字段
例:订单表用"用户ID"作为分片键
分片键选择原则
原则1:查询高频字段(大部分查询都带这个字段)
原则2:数据分布均匀(避免数据倾斜)
原则3:尽量不可变(分片键变了数据要迁移)
常见分片策略
范围分片:按范围划分(如 1-1000万在库1,1000万-2000万在库2)
优点:扩容方便
缺点:数据倾斜、热点集中
哈希分片:按哈希值取模(如 userId % 16)
优点:数据分布均匀
缺点:扩容困难(重新哈希导致数据迁移)
一致性哈希:减少扩容时的数据迁移
时间分片:按时间划分(适合日志、流水类)
分片键选择的权衡
例:订单表用"用户ID"分片 → 按用户查订单快,但按订单ID查需要额外处理
解决:基因法、异构索引表(冗余一份按其他维度的数据)
🆔 8.6 分布式 ID 方案(分库分表的前提)
为什么需要分布式 ID
分库分表后,各库的自增 ID 会重复
需要全局唯一的 ID
常见方案
UUID:全局唯一,但无序、长,不适合做主键
数据库自增(号段模式):从数据库批量获取 ID
雪花算法(Snowflake):
结构:时间戳 + 机器ID + 序列号
优点:趋势递增、高性能、不依赖数据库
缺点:依赖时钟(时钟回拨问题)
Redis 自增:用 Redis 生成自增 ID
选型建议
高并发场景:雪花算法(主流)
简单场景:号段模式
⚠️ 8.7 分库分表带来的新问题(必须面对)
问题1:跨库事务
一个操作涉及多个库,本地事务失效
解决:分布式事务(2PC、TCC、Saga)或最终一致性(消息)
问题2:跨分片查询
不带分片键的查询,要扫描所有分片
解决:冗余异构索引表、搜索引擎(ES)
问题3:跨分片排序/分页
例:查"最新的100条订单",数据分散在多个分片
解决:各分片查询后归并排序,或借助搜索引擎
问题4:扩容困难
增加分片数,数据需要迁移
解决:预分片(一开始就分足够多)、一致性哈希
架构师忠告
分库分表是"最后的手段",能不分就不分
先考虑:优化SQL、加索引、读写分离、缓存
实在不行再分库分表(因为它引入大量复杂性)
🔧 8.8 SQL 与索引优化(最基础也最重要)
慢 SQL 是数据库性能的头号杀手
索引优化
合理使用索引(查询字段建索引)
避免索引失效(如对索引列做函数运算)
联合索引的最左前缀原则
SQL 优化
避免 SELECT *
避免大事务
分页优化(深分页问题)
批量操作代替循环单条操作
核心认知:优化一条慢 SQL,可能比加一台机器更有效
🔧 九、服务治理:分布式环境的必修课
📖 9.1 为什么微服务需要服务治理
微服务拆分后,服务数量众多
服务间调用复杂,任何服务都可能故障
需要一套机制来管理、保护这些服务
🔍 9.2 服务注册与发现
问题:服务实例动态变化,如何找到对方?
注册中心:服务启动时注册,调用方从注册中心获取服务列表
常见组件:Nacos、Consul、Eureka、Zookeeper
🛡️ 9.3 熔断、限流、降级、隔离(服务容错四板斧)
熔断(Circuit Breaker)
问题:下游服务持续失败,调用方不断重试,拖垮自己
熔断原理:像电路保险丝,下游故障时"熔断",快速失败
三种状态:闭合(正常)→ 打开(熔断)→ 半开(试探恢复)
代表:Hystrix、Sentinel、Resilience4j
限流(Rate Limiting)
控制进入的流量,保护系统不被打垮
算法:见网关部分的限流算法
降级(Fallback)
问题:某个非核心功能故障,不能影响核心功能
降级:暂时关闭或简化非核心功能,保证核心可用
例:推荐系统挂了,降级为返回热门榜单
隔离(Bulkhead,舱壁模式)
问题:一个依赖变慢,占满线程池,拖垮整个服务
隔离:用独立的线程池/信号量隔离不同依赖
类比:船舱的隔板,一个舱进水不影响其他舱
四者的关系
限流:入口控制(挡流量)
熔断:出口控制(下游故障时快速失败)
降级:兜底方案(提供备选)
隔离:故障隔离(防止扩散)
⏱️ 9.4 超时与重试的正确姿势
超时
所有远程调用必须设置超时,避免无限等待
超时时间要合理(太短误判,太长拖慢)
重试
失败后重试,但要注意
只对"幂等"操作重试(避免重复扣款)
重试次数有限,配合指数退避
重试可能放大流量(重试风暴),需限流保护
核心认知:超时是底线,重试要谨慎
🛡️ 十、高可用的核心设计模式(横切总结)
📖 10.1 为什么高可用是核心
上亿用户的系统,宕机损失巨大
高可用 = 系统在部分组件故障时仍能对外提供服务
核心思想:假设一切都会失败,为失败做设计
🔄 10.2 冗余(避免单点)
思想:关键组件不要只有一个,要有备份
体现:集群部署、主从复制、多机房
原则:消除单点故障(SPOF)
任何"只有一个"的组件都是隐患
🛡️ 10.3 容错(快速失败)
思想:依赖故障时,不要无限等待,快速失败
体现:超时控制、熔断器
价值:避免故障扩散,保护自己
📉 10.4 降级(丢车保帅)
思想:资源有限时,牺牲非核心功能,保证核心功能
降级的层次
自动降级:熔断触发后自动降级
手动降级:大促前主动关闭非核心功能
例:大促时关闭推荐、评论、积分等非核心功能
🚦 10.5 限流(自我保护)
思想:超过系统处理能力的请求,直接拒绝
价值:宁可拒绝部分用户,也不能让整个系统崩溃
类比:餐厅排队,超过接待能力就不再接客
🧱 10.6 隔离(故障隔离)
思想:将系统分成独立的单元,故障不互相影响
体现:线程池隔离、服务分组、多机房
价值:把故障控制在局部
🛟 10.7 兜底(最终防线)
思想:所有手段都失效时,要有最后的兜底方案
体现:返回默认值、静态页面、友好提示
例:推荐系统全挂了,返回默认的热门列表
核心认知:永远要有 Plan B
🎯 10.8 高可用模式的组合使用
高可用不是单一技术,而是多种模式的组合
典型组合:冗余 + 熔断 + 降级 + 限流 + 兜底
架构师视角:为每个关键链路设计"故障预案"
🚀 十一、性能优化:从代码到系统
📖 11.1 性能优化的方法论
第一原则:先测量,后优化(不要盲目优化)
用数据说话:先定位瓶颈在哪
避免"过早优化"(优化了非瓶颈,白费功夫)
第二原则:优化瓶颈(木桶理论)
找到最薄弱的环节,优先优化它
第三原则:权衡(优化往往有代价)
例:加缓存提升性能,但引入一致性问题
优化流程:测量 → 定位瓶颈 → 优化 → 再测量 → 验证效果
🔍 11.2 性能瓶颈定位
四大资源维度
CPU 瓶颈:CPU 使用率高(死循环、复杂计算)
内存瓶颈:内存不足、频繁 GC
IO 瓶颈:磁盘/网络 IO 慢(慢 SQL、大量读写)
锁瓶颈:并发竞争(线程等待锁)
定位工具
CPU/内存:top、jstack、jmap、Arthas
慢 SQL:慢查询日志、explain
链路:链路追踪(定位慢在哪个服务)
核心认知:定位比优化更重要
💻 11.3 代码级优化
避免不必要的计算(缓存计算结果)
减少对象创建(对象池、复用)
选择合适的数据结构和算法
避免在循环中做重操作(如循环查数据库 → 改为批量查询)
🧠 11.4 JVM 优化(Java 应用)
合理的堆内存大小
选择合适的垃圾回收器(G1、ZGC)
减少 GC 频率和停顿时间
避免内存泄漏
📦 11.5 批量处理与并行计算
批量处理
把多次小操作合并为一次大操作
例:循环插入1000条 → 批量插入1000条
价值:减少网络往返、减少数据库压力
并行计算
把串行的多个独立操作改为并行
例:同时查询多个数据源,再汇总
注意:并行要考虑线程安全和资源
📊 十二、稳定性保障:让系统"不挂"
📖 12.1 为什么稳定性是高并发的生命线
高并发系统一旦故障,影响面巨大
稳定性不是"测"出来的,是"设计 + 运维"出来的
核心:监控、发现、定位、恢复
📈 12.2 监控告警体系
监控的层次
基础设施监控:CPU、内存、磁盘、网络
应用监控:QPS、RT、错误率
业务监控:订单量、支付成功率
告警
设置阈值,异常时告警
告警要分级(紧急/重要/一般)
避免告警风暴(告警太多等于没告警)
代表工具:Prometheus + Grafana
🔗 12.3 链路追踪
问题:一个请求跨多个服务,慢了/失败了,怎么定位?
链路追踪:给请求一个全局 TraceID,记录它在各服务的调用链
价值:快速定位"慢在哪、错在哪"
代表:SkyWalking、Jaeger、Zipkin
🏋️ 12.4 全链路压测
为什么需要压测
上线前验证系统容量
发现性能瓶颈
全链路压测
在生产环境模拟真实流量
覆盖完整链路(而非单点)
注意:压测数据要与真实数据隔离,避免污染生产数据
大促前必做全链路压测
🔥 12.5 混沌工程(Chaos Engineering)
思想:主动注入故障,验证系统的容错能力
例:主动杀掉一个服务、断网、增加延迟
目的:在可控环境下提前发现隐患
代表:ChaosBlade、Netflix Chaos Monkey
核心认知:以攻代守,主动发现问题
🚨 12.6 应急预案与故障演练
应急预案
针对各种故障场景,预先制定处理方案
例:数据库挂了怎么办、缓存挂了怎么办
故障演练
定期模拟故障,检验应急预案的有效性
训练团队的应急能力
故障复盘
每次故障后复盘,找到根因,改进系统
核心:从故障中学习
📈 十三、容量规划:上亿级的"数学题"
📖 13.1 为什么容量规划很重要
容量不足:系统被打垮
容量过剩:资源浪费,成本高
目标:用合理的资源,支撑业务的流量
🧮 13.2 容量评估方法(从业务量推导技术容量)
步骤1:明确业务量
例:日均订单 1000万,峰值是平均的 10 倍
步骤2:推导峰值流量
峰值订单 = 1000万 × 10 / 86400秒 ≈ 1150 TPS(平均)
峰值时刻可能集中,需进一步估算峰值 QPS
步骤3:评估单实例容量(通过压测)
例:单个订单服务实例能扛 500 TPS
步骤4:计算所需实例数
实例数 = 峰值 QPS / 单实例容量 × 冗余系数(如 1.5-2 倍)
步骤5:逐层推导(数据库、缓存、MQ 的容量)
核心认知:容量规划是"算"出来的,不是"猜"出来的
📐 13.3 容量模型与冗余
冗余系数:不要按极限容量设计,要留余量(通常 50%-100%)
原因:应对突发流量、故障转移
容量水位:设定告警水位(如 70%),提前扩容
📈 13.4 扩容策略
垂直扩容:加资源(快,但有上限)
水平扩容:加实例(需要提前设计好可扩展性)
弹性伸缩:根据流量自动扩缩容(云原生)
预案:大促前预扩容,大促后缩容
🎉 13.5 大促容量准备(实战)
大促前
全链路压测,验证容量
预扩容,预留足够资源
降级预案(关闭非核心功能)
限流预案(保护核心链路)
大促中
实时监控,随时响应
应急预案待命
大促后
复盘,优化
🎯 十四、实战案例:秒杀系统设计(综合检验)★核心
📖 14.1 秒杀场景的特点与难点
场景:少量商品(如100件),海量用户(如100万人)同时抢购
难点1:瞬时高并发
活动开始瞬间,百万请求同时涌入
难点2:超卖问题
库存只有100件,不能卖出101件
并发扣减库存容易超卖
难点3:恶意请求
黄牛用脚本刷,占用资源
难点4:数据一致性
库存扣减、订单创建要保证一致
核心思想:层层过滤,把流量挡在离数据库越远越好
🏗️ 14.2 秒杀系统的分层设计(流量漏斗)
第一层:前端(页面层)
静态化:秒杀页面静态化,用 CDN 缓存
按钮防抖:防止用户重复点击
答题/验证码:拉长请求时间,削峰,防脚本
价值:挡掉大量无效/重复请求
第二层:接入层(负载均衡)
限流:控制进入的流量
价值:挡住超量请求
第三层:网关层
鉴权:验证用户身份(拦截未登录)
限流:按用户限流(防刷)
价值:拦截非法请求
第四层:应用层
库存预判:用 Redis 判断库存,无库存直接返回
资格校验:是否已购买过
价值:挡掉大部分请求(库存很快售罄)
第五层:消息队列(削峰)
通过校验的请求,发送到 MQ
异步创建订单
价值:削峰,保护数据库
第六层:数据库层
最终扣减库存、创建订单
用数据库乐观锁/悲观锁防止超卖
价值:只处理真正有效的少量请求
🔢 14.3 库存扣减方案(防超卖核心)
方案1:数据库乐观锁
UPDATE stock SET count=count-1 WHERE id=1 AND count>0
利用 WHERE count>0 防止超卖
优点:简单可靠
缺点:高并发下数据库压力大
方案2:Redis 预扣减
库存放在 Redis,用 Redis 原子操作扣减
扣减成功再发 MQ 异步落库
优点:Redis 性能好,扛得住高并发
缺点:Redis 和数据库的一致性
方案3:Redis + Lua 脚本
用 Lua 脚本保证扣减的原子性
最常用的高并发方案
推荐:Redis(Lua)预扣减 + MQ 异步落库 + 数据库最终校验
🚦 14.4 限流与防刷
按用户限流:一个用户只能抢一次
按 IP 限流:防止单 IP 刷
验证码/答题:人机识别
风控:识别异常行为
🔄 14.5 秒杀的完整链路总结
用户请求 → CDN(静态页面)→ 负载均衡 → 网关(鉴权限流)→ 应用(Redis库存预判)→ MQ(削峰)→ 订单服务(创建订单)→ 数据库(扣减库存)
每一层都在过滤流量,最终到数据库的请求极少
核心思想:把流量挡在离用户最近的地方
💡 14.6 秒杀设计的架构师要点
要点1:尽早过滤(越早挡掉越好)
要点2:异步化(用 MQ 削峰)
要点3:防超卖(库存扣减的原子性)
要点4:防刷(限流 + 风控)
要点5:降级预案(秒杀系统故障不影响主站)
🧭 十五、架构师方法论:如何做出正确决策
📋 15.1 高并发系统的设计流程
步骤1:明确业务需求和目标
预估流量(平均、峰值)
明确性能要求(RT、QPS)
明确可用性要求(几个9)
步骤2:容量规划
从业务量推导技术容量
各层容量评估
步骤3:架构设计
分层设计(接入、应用、缓存、存储)
选择合适的技术(缓存、MQ、分库分表)
步骤4:技术选型
根据场景选择具体技术
考虑团队能力、生态、成本
步骤5:稳定性设计
冗余、容错、降级、限流
监控、告警、应急预案
步骤6:压测与优化
全链路压测
发现瓶颈,优化
步骤7:上线与运维
灰度发布
持续监控
⚖️ 15.2 权衡取舍的艺术(架构师的核心能力)
性能 vs 一致性
强一致性牺牲性能,高性能往往牺牲一致性
决策:根据业务容忍度选择(如秒杀可接受最终一致性)
性能 vs 成本
高性能需要更多资源(缓存、集群)
决策:在预算内找到最优解
可用性 vs 复杂度
高可用需要冗余、容错,增加复杂度
决策:核心系统高可用,非核心可简化
通用原则
没有完美的方案,只有最适合当前场景的方案
架构师的职责:在约束条件下找到最优权衡
🚫 15.3 避免过度设计
什么是过度设计
为不存在的问题引入复杂方案
例:日均1000单的系统上来就分库分表
过度设计的代价
复杂度失控、开发维护成本高
如何避免
从实际需求出发,不为"可能的未来"过度设计
YAGNI 原则(你不会需要它)
简单优先:能用简单方案解决的,不用复杂方案
架构师自省
"我引入这个复杂度,是为了解决当前的真实问题吗?"
🔄 15.4 演进式高并发(从低到高的演进路径)
核心认知:高并发架构是"演进"出来的,不是"一步到位"的
演进路径
阶段1:单体 + 垂直扩展(流量小)
阶段2:单体 + 负载均衡(水平扩展)
阶段3:引入缓存(读优化)
阶段4:读写分离(数据库优化)
阶段5:异步化 + MQ(削峰解耦)
阶段6:微服务拆分(业务复杂)
阶段7:分库分表(数据量大)
阶段8:多级缓存 + 服务治理(精细化)
架构师忠告
不要一开始就上全套高并发方案
根据流量增长,逐步演进
每次演进都解决当前的真实瓶颈
收藏
立即使用
收藏
立即使用
收藏
立即使用
收藏
立即使用
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页