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

1. 引言

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

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

2. 先搞清楚瓶颈在哪

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

常见的瓶颈点包括:

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

定位手段:

  • topvmstat 观察 CPU、上下文切换;
  • jstack 抓线程栈,看线程到底阻塞在哪;
  • 用 Arthas 的 dashboardthread 命令快速定位热点线程;
  • 用 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 非线程安全,改用 DateTimeFormatterThreadLocal
  • 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 连接池、业务线程池隔离
数据层 多级缓存、索引优化、读写分离
代码层 并发安全、并行化改造、热点优化

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

相关推荐
Terra.K1 小时前
ThreadLocal解决一个线程里面跨层传递问题
java·开发语言·后端
RuoyiOffice2 小时前
SpringBoot3+Vue3 最推荐的开源 OA 系统 2026:流程驱动、资源闭环、多端互通
spring boot·vue3·flowable·oa·协同办公·spring boot 3·开源oa
泡海椒2 小时前
低代码规则开发:JQuick-Java声明式配置驱动开发落地教程
java·低代码·rxjava
Lost of 程序猿2 小时前
ASP.NET Core API 版本管理与契约演进:船岸版本碎片化下的兼容性工程
后端·asp.net
吃饱了得干活2 小时前
Redis 从单机到集群:持久化、主从复制、哨兵与集群完全指南
redis·后端
weixin_440730502 小时前
socket解决多次输入问题-大数据接收问题-模块动态导入
java·开发语言·servlet
用户3126874877202 小时前
ReentrantLock 和 synchronized 到底选哪个?源码级对比与选型
java
老孙讲技术2 小时前
【监控开发】把车间未戴帽和烟火报警接进安监值班群:workwearDetect 对图与 setMessageCallback 订 smokeAlarm
后端·物联网·音视频开发
计算机毕设定制辅导-无忧学长2 小时前
基于Spring Boot的玄幻小说个性化推荐平台设计与实现
java·vue.js·spring boot·后端·mysql·推荐算法