高并发系统设计:从理论到实践的实战思考

孙军♪
2025-06-20 13:03
阅读 666

开篇:为何要写这样一篇文章?

开篇:为何要写这样一篇文章?

在我五年的后端开发经历中,最让我记忆深刻的就是那个曾经让我在凌晨两点还在看日志、改配置、调线程池的项目——一个面向全国用户的在线预约服务平台。这个系统的用户量在短时间内从几十万增长到了几百万,初期没做任何高并发设计的系统频频报错,响应延迟高、请求超时、数据库连接爆满,甚至出现雪崩式崩溃。

经历过那段“被压垮”的时光后,我开始认真思考:什么样的系统才能扛得住大流量?如何从一开始就把架构设计得更具扩展性和稳定性?

于是,这篇文章想和大家聊聊我亲身经历过的那些问题,以及我在实践中总结出来的一套“从理论到落地”的高并发系统设计思路。


问题描述:我们到底遇到了什么?

问题描述:我们到底遇到了什么?

我们的平台主要功能是提供医院挂号预约服务。上线初期每天访问量不到一万次,但随着宣传推广和接入更多城市的三甲医院,日均请求迅速上涨到了几十万甚至上百万。

最初的设计并没有过多考虑并发:

  • 单一入口 Nginx + Tomcat 架构
  • 数据库主从结构,无分表分库
  • 接口没有限流、熔断机制
  • 全部用同步调用处理流程

结果就是:

  • 系统响应时间从100ms飙升到2秒以上
  • 高峰期数据库CPU打满,QPS只有几百
  • 资源利用率极低,浪费严重
  • 用户反馈频繁卡顿,订单创建失败率高达30%

那是一个典型的“小马拉大车”的场景,必须重构。


解决方案:一步步构建能扛压的系统

解决方案:一步步构建能扛压的系统

1. 架构层面:从单体到微服务化

我们先把整个业务拆成多个子服务,比如用户服务、挂号服务、订单服务、支付回调服务等。这一步带来了几个好处:

  • 模块解耦,便于独立部署和扩缩容
  • 某个服务出问题不会整体瘫痪
  • 可以根据业务特性使用不同的技术栈(比如订单用缓存多,用Redis多;用户系统对一致性要求高,更注重事务)

引入 Dubbo 作为 RPC 框架,通过 Zookeeper 做注册中心,服务之间通过接口调用通信。

2. 流量控制:接入层+应用层双重防护

Nginx 层加限流与负载均衡

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=50r/s;

    upstream backend {
        least_conn;
        server 192.168.1.101 weight=3;
        server 192.168.1.102;
        server 192.168.1.103 backup; # 备份节点
    }

    server {
        listen 80;

        location /api/ {
            limit_req zone=one burst=10 nodelay;
            proxy_pass http://backend;
            proxy_set_header Host $host;
        }
    }
}

上面这段配置做了几个关键事情:

  • 使用 limit_req 控制每秒请求数,防止突发流量冲击
  • 设置多个服务节点并做负载均衡
  • 设置一个备份节点,用于降级切换

应用层加熔断与降级(Hystrix)

我们为每个服务间的远程调用都加上了 Hystrix 熔断器,设置好 fallback 方法:

@HystrixCommand(fallbackMethod = "fallbackForOrder")
public Order createOrder(OrderRequest request) {
    // 正常调用逻辑
}

private Order fallbackForOrder(OrderRequest request, Throwable e) {
    // 返回默认值或错误码
    log.warn("订单服务异常,触发降级", e);
    return new Order().setFallback(true);
}

这样做可以在某个服务不可用时快速失败,避免资源阻塞。

3. 缓存策略:降低数据库压力

我们在以下几个关键点引入缓存:

  • 用户信息缓存(Redis)
  • 医院号源信息缓存(热点数据)
  • 接口响应缓存(例如静态配置页)

为了防止缓存穿透,我们使用 Redis 的布隆过滤器进行拦截;对于缓存击穿,则采用“空值缓存”策略 + 过期时间错开的方式。

另外,我们引入了本地缓存 Caffeine 在一些高频读操作中,进一步减少网络 IO。

4. 数据库优化:读写分离 + 分库分表

原先是单一 MySQL 实例,后来拆成:

  • 一主两从,主写从读
  • 使用 MyCat 做读写分离中间件
  • 分库按业务维度切分,分表按时间维度进行

比如挂号记录这张表,每月的数据单独一张表,并建立对应的索引策略:

CREATE TABLE booking_record_202410 (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    hospital_id INT,
    doctor_id INT,
    booking_time DATETIME,
    status TINYINT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

并通过 Sharding-JDBC 来实现动态路由。

5. 异步处理:消息队列削峰填谷

我们将部分非实时性操作(如短信通知、订单状态更新、日志异步写入)抽离出来,使用 Kafka 进行异步处理。

Kafka 在我们系统里起到了以下作用:

  • 把耗时任务从主线程剥离出去
  • 缓解高峰期瞬间写压力
  • 提供失败重试机制

我们还基于 Kafka 做了一个简单的“事件驱动”架构,提升了系统之间的协作效率。


踩坑经验:这些坑别再踩了

踩坑经验:这些坑别再踩了

❌ 线程池配置不合理导致资源耗尽

早期我们直接用了 Spring Boot 默认的线程池参数,结果在并发压测中发现大量线程阻塞,服务器内存暴涨。

✅ 改进做法:

  • 自定义线程池,设置合理的 corePoolSize 和 maxPoolSize
  • 根据不同业务模块设定隔离的线程池(如订单处理和服务发现调用分开)
  • 加上拒绝策略和队列缓冲

代码示例:

@Bean("orderTaskExecutor")
public ExecutorService orderTaskExecutor() {
    int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
    return new ThreadPoolExecutor(
        corePoolSize,
        corePoolSize * 2,
        60L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(1000),
        new ThreadPoolExecutor.CallerRunsPolicy());
}

❌ 忘记监控和告警机制

刚开始没接入 Prometheus + Grafana 做指标采集,出了问题全靠人工排查。

✅ 后来补上了这些监控:

  • 请求成功率、RT、TPS
  • JVM 内存和GC情况
  • Redis 缓存命中率
  • Kafka 消息积压情况

并且结合企业微信告警推送,一旦触发阈值立即通知值班人员。


效果总结:重构后的收益

经过三个月的迭代重构,最终效果非常明显:

数据库设计模型-2

指标 改造前 改造后
QPS <500 12,000+
平均响应时间 2s+ 200ms以内
错误率 10%~30% <1%
系统可用性 不稳定 >99.95%
成本 高(浪费严重) 明显下降

更重要的是:现在我们可以随时弹性扩容,在双十一/春节这种高峰时间段也能稳住不挂,客户投诉率也明显下降。


经验分享:给读者的一些忠告

如果你也在做或者将要做一个需要支持高并发的系统,我建议你重点关注以下几个方面:

🧱 设计阶段就要考虑分布式能力

哪怕一开始是单体架构,也要留好未来拆分的伏笔。比如:

  • 接口抽象清晰,方便以后拆成RPC
  • 核心数据模型设计要有扩展性
  • 日志和链路追踪要提前接入

🔐 安全和治理不能忽略

高并发下更容易被攻击。一定要注意安全加固:

  • 接口幂等性处理
  • 黑名单IP限制
  • DDOS清洗
  • API网关的身份认证和权限控制

🧠 性能是细节堆出来的

很多性能问题是藏在细节里的。比如:

  • 没有合理使用索引,导致慢查询
  • 一次HTTP请求返回太多数据,浪费带宽
  • 同步等待可以改成异步处理却没做
  • Redis key过期时间太集中,造成缓存抖动

这些问题往往要靠经验去发现,靠工具去诊断。

📊 监控永远比日志重要

出现问题时,第一反应不是翻日志,而是打开监控大盘看趋势变化。好的监控系统应该告诉你:“哪里出了问题”,而不是“发生了啥”。


结语:设计是为了应对未知的变化

负载均衡配置-1

高并发系统的设计,从来不是一蹴而就的事情。它更像是一场马拉松,需要你在每一个环节都深思熟虑,预留扩展空间。

我始终相信一句话:优秀的架构,不是为了让系统跑得最快,而是为了让它在各种极端情况下也能保持稳定运行。

愿你我也能在复杂的世界里,写出稳健又优雅的代码。


如果你也有类似的项目经历,欢迎留言交流,我们一起成长!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝