高并发 架构设计二
四、数据库层(高并发最大瓶颈)
高并发环境下数据库层优化的核心:减少数据库请求量、减少单条 SQL 耗时、减少锁竞争、分散压力,让数据库在高并发下不卡顿、不夯死、不雪崩。
1. 分库分表
- 水平分表:解决单表过大、索引臃肿,如按用户 ID / 订单 ID 哈希取模分片
- 垂直分库:按业务拆库,隔离热点与瓶颈
- 哈希分片均匀分布数据,避免热点库
2. 索引优化
- 建立合适联合索引,遵循最左前缀原则
- 禁止无索引查询、禁止隐式转换导致索引失效
- 删掉无用索引,减少写入性能损耗
- 避免
select *,只查需要字段,减少回表
3. SQL 优化
- 消灭慢查询、大事务
- 减少 join,尤其禁止跨库 join
- 避免 in 大量数据、深度分页
4. 事务优化
- 尽量短事务,避免长事务占锁、占连接
- 读已提交 RC 通常比可重复读 RR 并发更好
- 分布式事务尽量用最终一致性(可靠消息、事务消息)
- 减少不必要事务,避免分布式事务滥用
- 禁止在事务中调用远程接口、RPC、外部 HTTP
5. 其他
- 关闭不必要的外键、约束
- 批量操作,减少交互次数。
- 归档历史数据,保持表“轻量”
- 使用读写分离、CDC(Change Data Capture,变更数据捕获) 同步
- 最终一致性代替强一致性。使用消息队列、事务消息实现最终一致
五、中间件 & 消息队列(削峰填谷)
在高并发场景下,中间件和 MQ 不直接处理业务,但负责 “削峰、解耦、异步、兜底”,让同步阻塞变成异步非阻塞,让瞬间洪峰变成匀速消费,从架构层面大幅提升系统吞吐量与稳定性。
1. 消息队列具体优化方式
(1) 同步转异步,减少阻塞(最核心)
- 下单后:扣减库存、支付、物流、通知、积分、日志等全部异步
- 主线程只做核心逻辑,快速返回
- 线程不再等待慢操作,吞吐量直线上升
(2) 削峰填谷,抗瞬时洪峰
- 秒杀、抢购、红包雨、大促流量直接进 MQ
- 消费者按数据库可承受的速度匀速消费
- 避免瞬间高并发打崩 DB、导致锁等待、雪崩
(3) 服务解耦,避免级联故障
- 上游不关心下游是否正常、执行多久
- 下游故障时,消息堆积不影响上游
- 恢复后继续消费,保证数据不丢失
(4) 流量错峰与限流控制
- 控制消费者并发数,保护下游资源
- 优先级队列:核心消息优先消费
- 延迟队列:实现定时关闭订单、超时任务
(5) 实现最终一致性,替代强事务
- 用可靠消息 / 事务消息保证跨服务数据一致
- 避免 TCC、SAGA 等高复杂度分布式事务
- 高并发下性能远强于强一致性事务
2. 其他中间件优化
(1) 分布式缓存(Redis)
- 挡住大部分读流量,大幅减轻 DB 压力
- 分布式限流、分布式锁、热点缓存
(2) 配置中心 / 注册中心(Nacos、Etcd)
- 统一配置,动态调参(限流、阈值、超时)
- 服务自动发现、负载均衡、扩缩容无感
(3) 服务网关 & 流量治理
- 统一入口、路由、限流、认证、黑名单
- 无效流量在入口层拦截,不进入内部系统
(4) 分库分表中间件(Sharding-JDBC)
- 自动路由、分片、并行查询
- 分散 DB 压力,提升并发能力
3. 高并发下 MQ 性能优化要点
(1) 生产者
- 异步发送、批量发送
- 刷盘策略优化、持久化权衡
(2) 消费者
- 合理并发线程数,不高不低
- 批量消费,减少 IO 次数
- 避免消费逻辑慢,造成消息堆积
(3) MQ 自身
- 集群化、主从、多副本保证高可用
- 分区 Topic 提升并行消费能力
- 死信队列、重试机制保证可靠
4. 消息队列常见问题
高并发下消息队列虽然能削峰解耦,但也面临消息丢失、重复消费、顺序错乱、消息堆积、数据不一致五大问题,通过confirm 持久化、手动 ACK、幂等去重、同 ID 同队列、优化消费速度、最终一致性等方案解决,保证高并发下可靠、有序、不堆积。
(1) 消息丢失(最常问)
① 场景
- 生产者发送失败
- MQ 内存宕机未持久化
- 消费者未处理完就确认
② 解决方案
- 生产者开启confirm 机制 / 事务消息,确保发送成功
- MQ 开启持久化,落盘后再应答
- 消费者手动 ACK,处理完业务再确认
- 死信队列 + 告警 + 定时补偿
注:死信队列是专门存放无法正常消费消息的队列,消息因被拒绝、过期、重试超限等成为死信。它能避免消息丢失、防止主队列阻塞,方便排查问题,是高并发、高可靠消息系统的标配。
(2) 消息重复消费(幂等问题)
① 场景
- 网络抖动重试、消费者超时重发、Rebalance 都会导致重复高并发下几乎无法避免
② 解决方案
- 接口保证幂等性:
- 唯一消息 ID 去重表
- Redis 记录已处理 ID
- 数据库唯一键约束
- 消费前先判断是否已处理
(3) 消息顺序错乱
① 场景
- 多分区、多线程并发消费
- 重试、重新平衡都会打乱顺序
② 解决方案
- 强顺序场景:
- 同一业务 ID哈希路由到同一个队列
- 单个队列单线程消费
- 不强依赖顺序的场景,用最终一致性代替顺序
(4) 消息大量堆积(高并发最容易炸)
① 场景
- 消费者处理太慢
- 下游阻塞(DB 慢查询、外部调用超时)
- 消费线程不足瞬间洪峰 > 消费能力 → 堆积暴涨
② 解决方案
- 紧急扩容消费者实例
- 优化消费逻辑,避免慢 SQL、阻塞调用
- 批量消费,提升吞吐量
- 临时把消息转发到 “重试队列”,先清空主队列
- 消费线程池参数调优
(5) 分布式事务 / 最终一致性问题
① 场景
上游发消息成功,下游执行失败,数据不一致
② 解决方案
- 事务消息 / 半消息(RocketMQ)
- 本地消息表 + 定时任务
- 可靠消息 + 重试 + 幂等
- 避免强一致,高并发用最终一致性
(6) 高并发下 MQ 其他问题
- 大消息:导致网络阻塞、消费变慢 → 拆小消息、传索引
- 死信泛滥:消费一直失败 → 死信队列 + 人工排查
- 集群雪崩:Broker 宕机 → 集群部署、多副本
- 性能瓶颈:分区不足、磁盘 IO 高 → 增加分区、SSD、批量发送
六、操作系统 & JVM 层(兜底优化)
高并发场景下,应用层优化到一定程度后,操作系统内核参数与 JVM GC、内存模型就会成为性能瓶颈。优化目标是:减少上下文切换、降低 GC 停顿、提升网络并发、避免资源耗尽。
1. JVM 层优化(高并发重点)
(1) 选用低延迟 GC(核心)
- 高并发优先 G1 / ZGC / Shenandoah
- ZGC 几乎无 STW,毫秒级停顿,适合低延迟高并发
- 避免使用 CMS(并发更新问题多,已废弃)
- 不推荐 Parallel GC(STW 长,不适合高并发)
(2) 合理设置堆内存
- 避免堆过小导致频繁 GC
- 避免堆过大导致单次回收变慢
- 建议:堆 ≦ 物理内存 50%,预留内存给系统、页缓存、线程栈
(3) 调整线程并发数
- 高并发下线程过多 → 大量上下文切换 + GC 压力飙升
- 线程数经验值:CPU 核心数 × 2 ~ 4
- 线程池队列不宜无限长,避免 OOM
(4) 避免大对象与频繁创建销毁
- 大对象直接进老年代,引发 FullGC
- 复用对象、使用池化(连接池、线程池、对象池)
- 减少临时集合、字符串拼接产生的中间对象
(5) 关闭不必要参数
- 关闭偏置锁
-XX:-UseBiasedLocking(高并发锁竞争强,反而耗性能) - 开启指针压缩
-XX:+UseCompressedOops节省内存 - 合理设置新生代比例,让短命对象在新生代回收
(6) 关键 JVM 优化总结
- GC 低延迟化
- 内存合理化
- 对象池化
- 锁开销最小化
- 减少 FullGC
2. 操作系统层优化(高并发加分项)
(1) 网络参数优化(高并发必调)
- 增大 TCP 并发连接数
- 调整
net.core.somaxconn监听队列 - 开启
tcp_tw_reuse复用 TIME_WAIT 端口 - 增大文件句柄数
nofile(高并发默认不够) - 调整 TCP 队列长度、缓冲区大小
目的:支持更高并发连接,减少端口耗尽、连接拒绝
(2) 减少上下文切换
- 降低线程数、避免线程过度竞争
- 使用线程绑定 CPU(taskset)
- 避免大量锁竞争、死循环、空轮询
(3) IO 与磁盘优化
- 使用 SSD 大幅提升随机 IO
- 调整 IO 调度算法(mq-deadline、none)
- 避免日志、GC 日志频繁刷盘
(4) 系统资源限制
- 提升进程最大文件句柄数
- 调整最大进程数、内存限制
- 禁止 OOM 机制随意杀应用进程
(5) CPU 调度优化
- 关闭 CPU 节能模式,开启性能模式
- 禁用 NUMA 平衡(避免内存跨节点访问延迟)
