TL;DR 高并发本质是降低单库的连接数——六层递进:系统拆分→缓存→MQ削峰→分库分表→异构存储→限流熔断,每层都在减少数据库的压力。

高请求并发就一定会有高并发问题吗?其实不是的。假设一个应用全部是纯内存计算,无论请求量多大,简单加机器就能线性扩展——那就不存在什么高并发问题。

高并发问题之所以存在,是因为系统中存在无法靠粗暴扩容解决的单点瓶颈。而这个瓶颈,在 90% 以上的场景中,都是数据库

核心思路:降低单库的连接数

所有高并发技术方案,归根结底都是在做同一件事——降低单个数据库实例需要承载的并发连接数。围绕这个目标,有六个层次的技术手段:

层次一:系统拆分(业务维度)

最粗粒度的优化:把不同业务模块的数据库物理分开。

1
2
3
4
5
单体架构:所有业务 → 一个数据库(1000 QPS)
拆分后:
用户服务 → 用户库(400 QPS)
订单服务 → 订单库(300 QPS)
商品服务 → 商品库(300 QPS)

每个业务独享自己的数据库,互不影响。拆分的难点在于分布式事务——以前一个事务搞定的事,现在跨库了。解决方案包括 Saga 模式、Seata、RocketMQ 事务消息等。

层次二:缓存(减少数据库命中率)

在应用和数据库之间加一层 Redis,热数据走缓存,只有冷数据才查库。

1
2
请求 → 缓存(命中 90%)→ 返回
→ 缓存(未命中 10%)→ 数据库 → 写入缓存 → 返回

缓存架构的几个关键决策:

问题 方案
缓存穿透 布隆过滤器、空值缓存
缓存击穿 互斥锁、逻辑过期
缓存雪崩 过期时间随机化、多级缓存
缓存一致性 Cache Aside(先更新 DB、再删缓存)

层次三:MQ 削峰(平滑流量)

瞬时流量如洪水般涌入时,消息队列充当”水库”:

1
10 万 QPS → MQ → 1000 QPS 匀速消费 → 数据库

以秒杀为例,10 万人同时下单,数据库每秒只能处理 1000 个。用 RocketMQ/Kafka 做缓冲,请求先进队列,后台服务按自己的节奏慢慢消费。用户体验变成”排队中”,而不是”系统崩溃”。

层次四:分库分表(同一业务内拆分)

当一个业务的单库也扛不住时,对同一业务做进一步拆分:

  • 垂直拆分:按字段拆——用户基础信息一张表,用户扩展信息一张表
  • 水平拆分(分表):按行拆——user_0, user_1, user_2… 按 user_id 取模路由
  • 读写分离:主库写、从库读——适合读多写少的场景

分库分表的关键是 sharding key 的选择——尽量让大部分查询落在单个分片上,避免跨分片查询。ShardingSphere 是目前最流行的分库分表中间件。

层次五:异构存储(不同引擎各司其职)

MySQL 不是万能的,不同类型的查询用不同的存储引擎:

数据引擎 擅长 场景
MySQL 事务、关联查询 核心业务数据
Elasticsearch 全文搜索、聚合分析 商品搜索、日志查询
ClickHouse OLAP 分析、海量聚合 用户行为分析、BI 报表
Redis 超高 QPS、简单 KV 缓存、计数器、排行榜
HBase 海量稀疏数据、宽表 用户画像、时序数据

层次六:限流与熔断(兜底保护)

前面的手段是”尽力而为”,限流是”保证下限”——即使扛不住,也别让整个系统崩溃。

常用的限流算法:

  • 令牌桶(Guava RateLimiter):匀速放令牌,请求来取令牌,没令牌就拒绝
  • 漏桶:固定速率漏水,请求像水倒进桶里,满了溢出
  • 滑动窗口(Sentinel):统计窗口内的请求数,超过阈值拒绝

熔断(Circuit Breaker):当某个下游服务持续失败时,不再徒劳地继续调用,快速失败,给下游喘息空间。Hystrix(已停更)→ Resilience4j → Sentinel,是熔断框架的演进路线。

高并发架构全景图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
             ┌──────────────┐
限流 ←───────│ 负载均衡 │───────→ 限流
└──────┬───────┘

┌─────────────┼─────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 订单服务 │ │ 商品服务 │ ← 系统拆分
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Redis │ │ MQ │ │ ES │ ← 缓存/削峰/异构存储
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
┌────▼─────────────▼─────────────▼────┐
│ MySQL(分库分表) │ ← 分库分表
│ user_0 user_1 order_0 order_1 │
└─────────────────────────────────────┘

池子(连接池/线程池)的调优

各种”池子”本质也是一种限流手段:

  • Tomcat 线程池:限制同时处理的 HTTP 请求数
  • 数据库连接池(HikariCP):限制同时打到数据库的连接数
  • 线程池:限制并发执行的任务数

调优原则:

  • 池子不能太大:超过数据库承载能力,大家都慢
  • 池子不能太小:资源利用不充分
  • 做好监控:activeCount、queueSize、waitCount —— 任何一个接近上限,就该准备扩容了

总结

高并发的本质是一道”流量管理题”。六个层次的技术手段,从粗到细逐步降低单库的连接数:

系统拆分 → 缓存 → MQ 削峰 → 分库分表 → 异构存储 → 限流熔断

理解了”降低单库连接数”这条主线,你会发现各种高并发技术方案不再是零散的技巧,而是一个层次分明的体系。