写点什么

SpringBoot 性能优化的 12 个小技巧

  • 2025-05-26
    福建
  • 本文字数:3428 字

    阅读完需:约 11 分钟

前言


不知道你在 SpringBoot 项目中,有没有遇到过下面这样的代码:


@GetMapping("/orders")public List<Order> listOrders() {    return orderDao.findAll(); }
复制代码


一次性查询了所有的订单,全表扫描 50 万数据,导致接口查询性能很差,严重的时候可能会导致 OOM 问题。


问题定位

  • 未分页查询

  • 无缓存机制

  • 未启用批量处理


这次事故让我明白:性能优化必须贯穿开发全流程


今天这篇文章,跟大家一起聊聊 SpringBoot 优化的 12 招,希望对你会有所帮助。



第 1 招:连接池参数调优


问题场景:默认配置导致连接池资源浪费,高并发时出现连接等待


错误配置

spring:  datasource:    hikari:      maximum-pool-size: 1000       connection-timeout: 30000
复制代码


数据库连接池的最大连接数,盲目设置过大,连接超时时间设置过长。


优化方案

spring:  datasource:    hikari:      maximum-pool-size: ${CPU核心数*2} # 动态调整      minimum-idle: 5      connection-timeout: 3000 # 3秒超时      max-lifetime: 1800000 # 30分钟      idle-timeout: 600000 # 10分钟空闲释放
复制代码


数据库连接池的最大连接数,改成根据 CPU 核心数动态调整。

将连接超时时间由 30000,改成 3000。


第 2 招:JVM 内存优化


问题场景:频繁 Full GC 导致服务卡顿

我们需要优化 JVM 参数。

启动参数优化

java -jar -Xms4g -Xmx4g -XX:NewRatio=1 -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=35-XX:+AlwaysPreTouch
复制代码


最大堆内存和初始堆内存都设置成了 4G。

-XX:NewRatio=1,设置新生代和老年代各占一半。

垃圾收集器配置的是 G1。

垃圾回收的最大停顿时间为 200 毫秒。


第 3 招:关闭无用组件

问题场景:自动装配加载不需要的 Bean

优化方案

@SpringBootApplication(exclude = {    DataSourceAutoConfiguration.class,    SecurityAutoConfiguration.class})public class Application {    public static void main(String[] args) {        SpringApplication.run(Application.class, args);    }}
复制代码


如果有些功能暂时用不到,可以先排除一下。

在 SpringBoot 项目启动的时候,排除了 DataSourceAutoConfiguration 和 SecurityAutoConfiguration 配置类的自动装载。


第 4 招:响应压缩配置

问题场景:接口返回 JSON 数据体积过大

优化方案

server:  compression:    enabled: true    mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/json    min-response-size: 1024
复制代码


配置开启响应的压缩。


第 5 招:请求参数校验

问题场景:恶意请求导致资源耗尽

防御代码

@GetMapping("/products")public PageResult<Product> list(    @RequestParam @Max(value=100, message="页大小不能超过100") int pageSize,    @RequestParam @Min(1) int pageNum) {    //...}
复制代码


在接口中做好参数校验,可以拦截很多恶意请求。


第 6 招:异步处理机制

问题场景:同步处理导致线程阻塞

优化方案

@Async("taskExecutor")public CompletableFuture<List<Order>> asyncProcess() {    return CompletableFuture.completedFuture(heavyProcess());}
@Bean("taskExecutor")public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(500); return executor;}
复制代码


在有些业务逻辑中,使用异步处理性能可能会更好。


第 7 招:使用缓存

使用缓存可以提升效率。

缓存架构



代码实现

@Cacheable(cacheNames = "products", key = "#id",            cacheManager = "caffeineCacheManager")public Product getDetail(Long id) {    return productDao.getById(id);}
复制代码


这里使用了内存缓存。


第 8 招:批量操作优化

问题场景:逐条插入导致性能低下

优化方案

@Transactionalpublic void batchInsert(List<Product> products) {    jdbcTemplate.batchUpdate(        "INSERT INTO product(name,price) VALUES(?,?)",        products,        500, // 每批数量        (ps, product) -> {            ps.setString(1, product.getName());            ps.setBigDecimal(2, product.getPrice());        });}
复制代码


每 500 条数据插入一次数据库。


第 9 招:索引深度优化

问题场景:慢查询日志频繁出现全表扫描,SQL 执行时间波动大

错误案例

-- 商品表结构CREATE TABLE products (    id BIGINT PRIMARY KEY,    name VARCHAR(200),    category VARCHAR(50),    price DECIMAL(10,2),    create_time DATETIME);
-- 低效查询SELECT * FROM products WHERE category = '手机' AND price > 5000 ORDER BY create_time DESC;
复制代码


问题分析



优化方案一:联合索引设计


索引创建

下面创建了一个分类 ID,单价和时间的联合索引:

ALTER TABLE products ADD INDEX idx_category_price_create (category, price, create_time);
复制代码


优化方案二:覆盖索引优化

查询改造

只查询索引包含字段:

SELECT id, category, price, create_time FROM products WHERE category = '手机' AND price > 5000 ORDER BY create_time DESC;
复制代码


这里使用了覆盖索引。


优化方案三:索引失效预防

常见失效场景



案例修复

错误写法:

SELECT * FROM products WHERE DATE(create_time) = '2023-01-01';
复制代码


正确写法:

SELECT * FROM products WHERE create_time BETWEEN '2023-01-01 00:00:00' AND '2023-01-01 23:59:59';
复制代码


查询时间范围,这里使用了 BETWEEN AND 关键字,代替了等于号。


优化方案四:索引监控分析

诊断命令

查看索引使用情况:

SELECT     index_name,    rows_read,    rows_selected FROM     sys.schema_index_statistics WHERE     table_name = 'products';
复制代码


分析索引效率:

EXPLAIN FORMAT=JSON SELECT ...;
复制代码


索引优化黄金三原则


  1. 最左前缀原则:联合索引的第一个字段必须出现在查询条件中

  2. 短索引原则:整型字段优先,字符串字段使用前缀索引

  3. 适度索引原则:单个表索引数量不超过 5 个,总索引长度不超过表数据量 30%


DBA 工具箱

  • 索引分析脚本

  • 执行计划可视化工具

  • 索引碎片检测工具


第 10 招:自定义线程池


问题场景:默认线程池导致资源竞争

优化方案

@Bean("customPool")public Executor customThreadPool() {    return new ThreadPoolExecutor(        10, // 核心线程        50, // 最大线程        60, TimeUnit.SECONDS,        new LinkedBlockingQueue<>(1000),        new CustomThreadFactory(),        new ThreadPoolExecutor.CallerRunsPolicy());}
复制代码


在高并发业务场景中,使用 Executors 类创建默认的线程池,可能会导致 OOM 问题。

因此,我们需要自定义线程池。


第 11 招:熔断限流策略


问题场景:突发流量导致服务雪崩

解决方案

// 使用Sentinel实现接口限流@SentinelResource(value = "orderQuery",                   blockHandler = "handleBlock",                  fallback = "handleFallback")@GetMapping("/orders/{id}")public Order getOrder(@PathVariable Long id) {    return orderService.getById(id);}
// 限流处理public Order handleBlock(Long id, BlockException ex) { throw new RuntimeException("服务繁忙,请稍后重试");}
// 降级处理public Order handleFallback(Long id, Throwable t) { return Order.getDefaultOrder();}
复制代码


为了解决重复流量导致服务雪崩的问题,我们需要增加接口熔断、限流和降级处理。


第 12 招:全链路监控体系


问题场景:线上问题定位困难,缺乏数据支撑

我们需要增加项目全链路的监控。

监控方案

# SpringBoot配置management:  endpoints:    web:      exposure:        include: "*"  metrics:    export:      prometheus:        enabled: true
复制代码


这里使用了 prometheus 监控。


监控架构



核心监控指标



总结


SpringBoot 性能优化检查清单

  • 连接池参数按业务调整

  • JVM 参数经过压测验证

  • 所有查询走缓存机制

  • 批量操作替代逐条处理

  • 线程池按场景定制

  • 全链路监控覆盖



三条黄金法则

  1. 预防性优化:编码时考虑性能影响

  2. 数据驱动:用监控指标指导优化方向

  3. 持续迭代:性能优化是持续过程


性能工具包

  • Arthas 在线诊断

  • JProfiler 性能分析

  • Prometheus 监控体系

(看着监控大屏上平稳的 QPS 曲线,我知道今晚可以睡个好觉了...)


文章转载自:苏三说技术

原文链接:https://www.cnblogs.com/12lisu/p/18895475

体验地址:http://www.jnpfsoft.com/?from=001YH

用户头像

还未添加个人签名 2025-04-01 加入

还未添加个人简介

评论

发布
暂无评论
SpringBoot性能优化的12个小技巧_spring_电子尖叫食人鱼_InfoQ写作社区