如何提升单体 Spring Boot 应用的并发数?

1. 引言

单体 Spring Boot 应用在业务初期往往是最务实的选择:部署简单、调试方便、团队上手快。然而随着用户量增长,并发请求一上来,应用响应变慢、CPU 飙升、数据库连接池被打满,各种问题接踵而至。很多人第一反应是「拆微服务」,但拆服务成本高、周期长,未必是当下最优解。

其实在单体架构内,通过合理的配置调优、线程池治理、连接池优化、缓存引入和代码层面的并发改造,往往就能把并发能力提升数倍甚至一个数量级。本文从配置层、容器层、数据层、代码层四个维度,系统梳理单体 Spring Boot 提升并发数的实战手段。

2. 先搞清楚瓶颈在哪

提升并发数之前,先要回答一个问题:当前系统的瓶颈到底在哪? 盲目调参往往事倍功半。

常见的瓶颈点包括:

  • CPU 密集:大量计算、加解密、序列化,CPU 使用率长期 90% 以上。
  • IO 密集:频繁访问数据库、Redis、外部 HTTP 接口,线程大量阻塞在等待 IO。
  • 内存受限:GC 频繁、堆内存不足,导致 Full GC 拖垮吞吐。
  • 连接池耗尽:数据库连接池、HTTP 连接池被打满,请求排队超时。
  • 锁竞争:同步块、分布式锁、数据库行锁竞争激烈,线程大量阻塞。

定位手段:

  • 用 top、vmstat 观察 CPU、上下文切换;
  • 用 jstack 抓线程栈,看线程到底阻塞在哪;
  • 用 Arthas 的 dashboard、thread 命令快速定位热点线程;
  • 用 APM 工具(SkyWalking、Prometheus + Grafana)观察接口耗时分布。

原则:先度量,再优化。没有数据支撑的调优都是碰运气。

3. 配置层:JVM 与 Spring Boot 基础调优

3.1 JVM 参数

合理的堆内存设置能显著减少 GC 停顿。示例:

bash 复制代码
java -Xms4g -Xmx4g -Xmn2g \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=100 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -jar app.jar

要点:

  • -Xms 与 -Xmx 设为相同值,避免运行时动态扩容带来的性能损耗;
  • 根据业务对象生命周期调整新生代大小;
  • 开启 GC 日志,便于事后分析。

3.2 Spring Boot 内嵌 Tomcat 调优

Spring Boot 默认使用内嵌 Tomcat,核心配置如下:

yaml 复制代码
server:
  tomcat:
    threads:
      max: 200          # 最大工作线程数
      min-spare: 20     # 最小空闲线程数
    accept-count: 200   # 等待队列长度
    max-connections: 10000  # 最大连接数
    connection-timeout: 5000

注意:线程数并非越大越好。线程过多会导致上下文切换开销剧增,反而降低吞吐。一般经验值:

  • CPU 密集型:线程数 ≈ CPU 核数 + 1
  • IO 密集型:线程数 ≈ CPU 核数 × 2(配合虚拟线程可进一步放宽)

3.3 启用虚拟线程(JDK 21+)

如果项目已升级到 JDK 21,可以开启虚拟线程,极大提升 IO 密集型场景的并发能力:

yaml 复制代码
spring:
  threads:
    virtual:
      enabled: true

开启后,每个请求不再独占一个平台线程,而是由虚拟线程承载,线程数上限大幅提升,非常适合大量阻塞在数据库/Redis/远程调用的场景。

4. 容器层:连接池与线程池治理

4.1 数据库连接池

数据库连接池是单体应用最常见的瓶颈。以 HikariCP(Spring Boot 默认)为例:

yaml 复制代码
spring:
  datasource:
    hikari:
      maximum-pool-size: 50
      minimum-idle: 10
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

连接池大小经验公式:

复制代码
连接数 = ((核心线程数 × 2) + 有效磁盘数)

但更务实的做法是:压测得出最优值。连接池过小会排队,过大则数据库本身成为瓶颈。

4.2 HTTP 客户端连接池

如果单体应用需要调用外部服务,务必使用带连接池的 HTTP 客户端(如 RestTemplate + HttpClient、WebClient + Reactor Netty),避免每次请求都新建连接。以 Apache HttpClient 为例:

java 复制代码
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(50);

4.3 业务线程池隔离

把不同业务的线程池隔离,避免某个慢接口拖垮整个应用。例如用 ThreadPoolTaskExecutor 单独处理异步任务:

java 复制代码
@Bean("asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(10);
    executor.setMaxPoolSize(50);
    executor.setQueueCapacity(200);
    executor.setThreadNamePrefix("async-");
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.initialize();
    return executor;
}

5. 数据层:缓存与查询优化

5.1 引入本地缓存 + Redis 多级缓存

缓存是提升并发最立竿见影的手段。热点数据优先走本地缓存(Caffeine),未命中再查 Redis,最后才落到数据库:

java 复制代码
@Cacheable(cacheNames = "user", key = "#id")
public User getUserById(Long id) {
    return userMapper.selectById(id);
}

5.2 数据库查询优化

  • 为高频查询字段建立合适的索引;
  • 避免 SELECT *,只查必要字段;
  • 分页查询用覆盖索引或游标分页,避免深分页;
  • 复杂统计查询考虑异步落库 + 定时汇总。

5.3 读写分离

如果读多写少,可以配置主从数据源,读请求走从库,写请求走主库,显著降低主库压力。

6. 代码层:并发改造与性能热点优化

6.1 接口幂等与并发安全

高并发下要特别注意共享资源的线程安全:

  • SimpleDateFormat 非线程安全,改用 DateTimeFormatter 或 ThreadLocal;
  • HashMap 并发写会丢数据,改用 ConcurrentHashMap;
  • 数据库扣减库存用乐观锁或 UPDATE ... WHERE stock > 0 原子操作。

6.2 串行改并行

多个无依赖的远程调用或查询,可以用 CompletableFuture 并行执行,缩短接口响应时间:

java 复制代码
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> userService.get(id));
CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(() -> orderService.get(id));

User user = userFuture.get();
Order order = orderFuture.get();

6.3 热点代码优化

  • 减少不必要的对象创建,避免频繁 GC;
  • 用 StringBuilder 替代字符串拼接;
  • 避免在循环中做数据库查询(N+1 问题);
  • 大对象、大集合操作尽量异步化或分片处理。

7. 压测验证:让数据说话

调优完成后,必须用压测验证效果。推荐工具:

  • JMeter:简单易用,适合接口级压测;
  • wrk:命令行工具,适合快速压测;
  • Arthas:线上问题定位,观察线程、GC、方法耗时。

压测要点:

  • 逐步增加并发数,观察吞吐量(TPS)和响应时间(RT)的变化曲线;
  • 关注 P99 延迟,而不是只看平均值;
  • 压测环境尽量贴近生产(数据量、硬件配置)。

8. 总结

单体 Spring Boot 提升并发数,核心思路是先定位瓶颈,再分层优化:

层次 关键手段
配置层 JVM 参数、Tomcat 线程数、虚拟线程
容器层 数据库/HTTP 连接池、业务线程池隔离
数据层 多级缓存、索引优化、读写分离
代码层 并发安全、并行化改造、热点优化

单体架构的并发天花板并不低,关键在于系统性地排查瓶颈、有针对性地调优。当单体经过充分优化仍无法满足业务需求时,再考虑拆分微服务也不迟。

相关推荐
Moment几秒前
如果你在做 RAG,可能会需要 pdf-inspector
前端·后端·面试
信誓旦旦的程序猿5 分钟前
【Python 量化取数指南 #09】Python 拿港股通数据:港股通成交与港股财报实测
java·python·股票数据api·股票数据·股票数据api接口·股票api数据接口·股票量化数据api
FYKJ_20106 分钟前
springboot羽毛球场地管理系统00626-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
Wx-bishekaifayuan14 分钟前
django医院营收信息预测系统49414-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
Wx-bishekaifayuan15 分钟前
springboot会议室预约管理系统42030-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
AINative软件工程22 分钟前
LLM 输出 Guardrail 工程实践:5 层防护栏让 AI 生成的内容不会炸掉生产
后端·llm·ai编程
IT_陈寒44 分钟前
Vite热更新失效?你可能漏了这个配置项
前端·人工智能·后端
Wx-bishekaifayuan44 分钟前
springboot生活商城系统21035-计算机课程设计、毕业设计
spring boot·后端·python·spring·elasticsearch·golang·课程设计
小程序设计1 小时前
本科毕设避坑|大数据 / 单片机物联网 / 网络工程 / PLC / 机械设计选题与落地心得
java·hive·hadoop·单片机
FYKJ_20101 小时前
django药饮个性化推荐系统54839-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django