高并发系统设计:从理论到实践 —— 我的实战经验分享
背景介绍:为什么要聊这个话题?

我是一名后端工程师,入行已经五年了。这五年中,从早期的小型项目到后来支撑百万级日活的互联网产品,我一直在和“高并发”打交道。尤其是在加入现在这家电商公司之后,经历过多次大促、秒杀等极限场景的洗礼,对高并发系统的设计也有了更深刻的理解。
我们团队负责的是一个电商平台的核心交易模块,包括下单、支付、库存扣减等关键路径。在日常流量下表现还不错,但一到大促期间,比如“双十一”,流量暴涨十几倍,系统的稳定性就变得尤为关键。而我在其中主要参与了接口性能优化、数据库调优以及服务扩容等多个关键环节。
今天想借这篇文章,结合自己亲身经历的一个真实项目,聊聊高并发系统设计到底是怎么一回事。
问题描述:一次失败的大促让我印象深刻

那是在我们平台的第一次“双十一”预热活动。当时我们的服务架构还算简单:
- 单体 Java 应用(Spring Boot)
- MySQL 主从 + Redis 缓存
- Nginx 做负载均衡 + 简单限流
活动一开始,订单服务迅速被打爆。用户不断刷新页面,大量请求涌入服务器,导致 CPU 爆表、线程池耗尽、MySQL 出现死锁、Redis 大量缓存穿透……整个系统几乎瘫痪。
最糟糕的是,很多用户付款成功了但没生成订单,还有一些用户被重复扣款。这不仅影响了用户体验,还造成了非常大的舆论压力和资损风险。
那次事故之后,我们痛定思痛,决定重构整个订单服务,目标是能够应对千万级 QPS 的并发访问。
解决方案:如何构建一个能扛住高并发的系统?

架构调整:从单体到微服务+异步解耦
首先我们将原来的单体应用拆分为多个独立的服务模块:
- 订单创建服务(Order Service)
- 支付回调服务(Payment Service)
- 库存服务(Inventory Service)
- 用户中心服务(User Center)
每个服务都部署为独立的 Spring Cloud 微服务,并通过 Feign 实现远程调用。引入 RabbitMQ 做消息队列,将同步调用改为异步处理,尤其适用于下单后的后续流程(如积分奖励、短信通知、推送数据给风控系统)。
示例:使用 RabbitMQ 异步处理订单完成事件
// 下单完成后发送消息
public void createOrder(OrderDTO orderDTO) {
Order order = saveOrder(orderDTO);
rabbitTemplate.convertAndSend("order.created", order.getId());
}
// 消费者监听消息进行后续处理
@Component
public class OrderConsumer {
@RabbitListener(queues = "order.created")
public void handleOrderCreated(Long orderId) {
// 发送短信、更新积分、推送到风控系统...
}
}
这样的设计有效减少了主线程阻塞时间,提升了整体吞吐能力。
接口层面:限流与降级策略
面对突发流量,限流和降级是必须的。我们在网关层使用了 Sentinel 进行限流控制,限制单位时间内最多请求次数,并根据当前系统状态动态调整阈值。
降级方面,我们通过 Hystrix 配合 Sentinel,在服务异常或超时时自动切换到备用逻辑,比如返回缓存数据、跳过非核心步骤等。
# 示例:Sentinel 规则配置(每秒最多1000个请求)
resources:
- name: /order/create
limitApp: default
grade: 1 # 1表示按QPS
count: 1000
这种机制帮助我们在后续几次大促中避免了服务雪崩。
数据库设计:读写分离+分库分表
MySQL 是系统的瓶颈之一。我们采取了以下措施:
- 读写分离:主库写,从库读,降低主库压力。
- 分库分表:使用 ShardingSphere 按照用户ID做水平拆分,将订单表分成多个物理表,减少单表容量。
- 冷热分离:把历史数据迁移到单独的冷备库,只保留近90天的活跃订单。
- 索引优化:频繁查询字段建立组合索引,避免全表扫描。
- 缓存兜底:热点订单使用本地缓存(Caffeine)+ Redis 双重缓存结构,防止缓存击穿。
-- 分库分表示例:按 user_id % 4 拆分成4张子表
CREATE TABLE orders_0 (...);
CREATE TABLE orders_1 (...);
...
-- 查询时根据 user_id 定位子表
SELECT * FROM orders_#{user_id % 4} WHERE ...
这些优化让我们的 DB 抗压能力得到了显著提升。
服务治理:监控与弹性伸缩
我们使用 Prometheus + Grafana 搭建了完整的监控体系,涵盖 JVM、线程池、慢SQL、接口延迟、TPS 等指标,实时预警。并通过 Kubernetes 自动扩缩容来应对流量峰值。
Kubernetes 的 HPA(Horizontal Pod Autoscaler)根据 CPU 使用率自动增加 Pod 数量,极大缓解了突发流量带来的压力。
# Kubernetes HPA 配置示例
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 2
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
这套机制在双十一大促中发挥了重要作用。
踩坑经验:那些年我踩过的坑
1. Redis 缓存击穿差点搞挂 MySQL
在初期,我们使用 Redis 缓存热点商品信息,但在某次缓存失效的时候,大量请求直接打到了 MySQL,造成短暂不可用。
解决方法:
- 设置永不过期,后台异步更新
- 使用布隆过滤器防空查
- 加互斥锁控制重建缓存并发
// 伪代码:加互斥锁重建缓存
public Product getProduct(Integer id) {
String cacheKey = "product:" + id;
Object product = redis.get(cacheKey);
if (product == null) {
synchronized(this) {
product = redis.get(cacheKey); // double-check
if (product == null) {
product = db.getProduct(id);
redis.setex(cacheKey, 60 * 60, product);
}
}
}
return product;
}
2. RabbitMQ 死信队列处理不当导致消息积压
有一段时间,我们因为消费者异常退出导致大量消息堆积。最初没有设置 TTL 和死信队列,导致消息一直卡在 Broker 中,最后需要手动清理。
改进方式:
- 设置消息最大尝试次数(max retries)
- 消息失败后进入死信队列,供人工介入处理
- 使用幂等性去重消费
3. Feign 调用默认超时太短,服务依赖链路长导致超时雪崩
Feign 默认的连接和读取超时时间都很短,当多个微服务存在级联调用时,很容易出现层层超时。
解决方案:
- 统一设置合理的超时时间
- 使用 Resilience4j 或 Sentinel 增强熔断能力
- 关键服务间采用 Dubbo 替代 Feign 提高性能
效果总结:我们到底获得了什么?
经过一系列改造后,我们再次迎接大促的压力测试:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 最高 TPS | 300/秒 | 8000+/秒 |
| 平均响应时间 | >800ms | <120ms |
| 错误率 | 12% | <0.5% |
| 服务可用性 | 95% | 99.99% |
更为关键的是,系统具备了更好的可观测性和自愈能力,运维同学也能快速定位问题点,再也不用半夜提心吊胆接电话了 😅。
经验分享:给后端朋友们的一些建议
1. 高并发的本质是系统设计的平衡术
不要追求极致性能,而是要从成本、稳定、可维护性等多维度考虑。有时候一个合理的降级比一个不稳定的高性能更好。
2. 性能优化要从源头抓起
接口慢,不一定就是程序的问题;可能数据库索引不对、网络有瓶颈、锁竞争严重。建议大家用 APM 工具(比如 SkyWalking、Pinpoint)追踪调用链,找到真正的性能瓶颈。
3. 不要迷信任何中间件
像 Kafka、Redis、Zookeeper 这些工具虽然强大,但不是万金油。它们也有适用边界。比如,Redis 不适合用来做复杂事务操作,Kafka 不适合低延迟场景。
4. 预案永远比出事后再补救好
高并发系统一定要有完善的应急预案,比如:
- 流量削峰(排队机制、令牌桶)
- 熔断降级(提前演练)
- 数据补偿机制(比如对账、补偿Job)
这些都要提前设计好,不能临时抱佛脚。
5. 多参与压测和故障演练
我们定期会组织故障注入演练(如模拟数据库宕机、Redis集群断连),通过这些模拟极端情况的方式不断提升系统的健壮性。
写在最后:技术是手段,业务才是目标
作为一名后端开发者,我一直坚信一句话:“再牛的技术也要服务于业务”。高并发系统不是为了炫技,而是为了让用户买得顺、付得快、体验好。
这些年走过的弯路、踩过的坑,都成为了我们团队宝贵的财富。希望这篇来自一线开发者的分享,能帮你在自己的项目中少走一些弯路。
如果你也在做高并发相关的工作,欢迎留言交流,一起进步!
—— By 一个爱写代码也爱思考的后端人

评论 0