架构师必知:SpringBoot性能优化的12招

大模型关系型数据库NoSQL数据库

大家好,我是苏三,又跟大家见面了。

前言

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

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

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

问题定位

  • 未分页查询
  • 无缓存机制
  • 未启用批量处理

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

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

picture.image

最近建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了不少的工作内推岗位。扫码加苏三的微信:li_su223,备注:所在城市,即可进群。

picture.image

第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招:使用缓存

使用缓存可以提升效率。

缓存架构

picture.image

代码实现

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

这里使用了内存缓存。

第8招:批量操作优化

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

优化方案

  
@Transactional  
public 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执行时间波动大

错误案例

  
-- 商品表结构  
CREATETABLE products (  
    idBIGINT PRIMARY KEY,  
    nameVARCHAR(200),  
    categoryVARCHAR(50),  
    price DECIMAL(10,2),  
    create\_time DATETIME  
);  
  
-- 低效查询  
SELECT * FROM products   
WHEREcategory = '手机'  
AND price > 5000  
ORDERBY create\_time DESC;  

问题分析

picture.image

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

索引创建

下面创建了一个分类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;  

这里使用了覆盖索引。

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

常见失效场景

picture.image

案例修复

错误写法:

  
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) {  
    thrownew RuntimeException("服务繁忙,请稍后重试");  
}  
  
// 降级处理  
public Order handleFallback(Long id, Throwable t) {  
    return Order.getDefaultOrder();  
}  

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

第12招:全链路监控体系

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

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

监控方案

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

这里使用了prometheus监控。

监控架构

picture.image

核心监控指标

picture.image

总结

SpringBoot性能优化检查清单

  • 连接池参数按业务调整
  • JVM参数经过压测验证
  • 所有查询走缓存机制
  • 批量操作替代逐条处理
  • 线程池按场景定制
  • 全链路监控覆盖

picture.image

三条黄金法则

  1. 预防性优化 :编码时考虑性能影响
  2. 数据驱动 :用监控指标指导优化方向
  3. 持续迭代 :性能优化是持续过程

性能工具包

  • Arthas在线诊断
  • JProfiler性能分析
  • Prometheus监控体系

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

最后欢迎加入苏三的星球,你将获得:AI开发项目课程、苏三AI项目、商城微服务实战、秒杀系统实战、商城系统实战、秒杀系统实战、代码生成工具、系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题、面试八股文等多个优质专栏。

还有1V1答疑、修改简历、职业规划、送书活动、技术交流。

扫描下方二维码,即可加入星球:

picture.image

目前星球已经更新了5200+篇优质内容,还在持续爆肝中.....

星球已经被官方推荐了3次,收到了小伙伴们的一致好评。戳我加入学习,已有1700+小伙伴加入学习。

最后推荐一下我的技术专栏《性能优化35讲》,里面包含了:接口调用、Java、JVM、并发编程、MySQL、Redis、ElasticSearch、Spring、SpringBoot等多个性能优化技巧。无论在工作,还是在面试中,都会经常遇到,非常有参考价值。

picture.image

picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动 NoSQL 的实践与探索
随着 NoSQL 的蓬勃发展越来越多的数据存储在了 NoSQL 系统中,并且 NoSQL 和 RDBMS 的界限越来越模糊,各种不同的专用 NoSQL 系统不停涌现,各具特色,形态不一。本次主要分享字节跳动内部和火山引擎 NoSQL 的实践,希望能够给大家一定的启发。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论