Java 接口性能优化二

三、数据库层面:优化数据交互的「最后一公里」

数据库是接口性能的「重灾区」—— 超过 60% 的接口响应慢问题可追溯至低效的数据交互。优化需从「SQL 执行效率」「索引设计」「连接管理」三个维度突破。

  1. SQL 优化:让查询「少走弯路」
    核心原则:减少无效数据扫描,让数据库「只做必要的工作」。

常见问题与优化:

  • ** 避免 SELECT ***:
    问题:返回冗余字段,增加数据传输量,无法利用覆盖索引。
    优化:明确指定需要的字段,如SELECT id, name, create_time FROM user WHERE status = 1

  • 通过 EXPLAIN 分析 SQL
    关键指标:

    • type:查询类型(从优到差:system > const > eq_ref > ref > range > index > all),all 表示全表扫描(需优化);
    • key:实际使用的索引(为 NULL 表示未走索引);
    • rows:预估扫描的行数(越小越好)。
  • 拆分复杂多表关联

    -- 低效:复杂多表关联
    SELECT o.id, o.amount, u.name, p.title 
    FROM order o
    JOIN user u ON o.user_id = u.id
    JOIN product p ON o.product_id = p.id
    WHERE o.status = 1 AND o.create_time > '2023-01-01';
    
     

    优化为:

    • 先查订单表:SELECT id, user_id, product_id, amount FROM order WHERE status = 1 AND create_time > '2023-01-01'
    • 提取 user_id 和 product_id,批量查用户表和商品表;
    • 在应用层用代码合并结果(如用 Map 缓存)。
  • 批量操作替代循环单条操作

    INSERT INTO order (user_id, amount) 
    VALUES (1, 100), (2, 200), ..., (1000, 50); -- 批量插入1000条
    
  1. 索引设计:平衡查询与写入效率
    索引是「加速查询的利器」,但并非越多越好 —— 过多索引会导致写入操作变慢(需维护索引结构)。

索引设计原则:

  • 针对 WHERE、ORDER BY、JOIN 字段建索引
    WHERE status = 1 AND create_time > '2023-01-01'可建(status, create_time)联合索引。

  • 遵循最左前缀原则
    联合索引(a, b, c)可匹配:

    • a = ?(最左前缀);
    • a = ? AND b = ?
    • a = ? AND b = ? AND c = ?
      但无法匹配b = ?b = ? AND c = ?
  • 避免冗余索引
    已存在联合索引(a, b)时,单独的a索引是冗余的;索引(a, b)(a, b, c)中,前者是后者的前缀,若后者存在,前者可删除(除非需利用前者的覆盖索引)。

  1. 连接池优化:避免「连接争抢」
    数据库连接是「稀缺资源」,配置不当会导致连接数不足(请求等待超时)或连接闲置(资源浪费)。

以 HikariCP 为例,核心参数配置:

参数作用推荐配置
minimumIdle最小空闲连接数核心业务并发量的 1/3(如并发 30 则设为 10)
maximumPoolSize最大连接数50-80(不超过数据库允许的最大连接数)
connectionTimeout获取连接的超时时间3-5 秒
idleTimeout空闲连接超时时间300 秒(5 分钟)
maxLifetime连接最大生命周期1800 秒(30 分钟,小于数据库 wait_timeout)

监控连接池状态:
通过 Spring Boot Actuator 暴露 HikariCP 指标:

management:
  endpoints:
    web:
      exposure:
        include: hikaricp

访问http://localhost:8080/actuator/hikaricp查看连接使用率、等待队列长度等,若等待队列持续增长,需调大 maximumPoolSize。

四、框架与中间件:让「配置」适配业务
  1. 缓存优化:减轻数据库压力
    缓存是「抗高并发的利器」,但需解决「穿透、击穿、雪崩」三大问题。
  • 缓存穿透:查询不存在的数据,缓存失效,直击数据库。
    解决方案:

    • 缓存空值(设置短期过期时间);
    • 布隆过滤器:提前过滤不存在的 key,如用 Redis 的 BF.ADD 和 BF.EXISTS 命令。
  • 缓存击穿:热点数据过期瞬间,大量请求直击数据库。
    解决方案:

    • 热点数据永不过期,后台定时任务异步更新;
    • 互斥锁:用 Redis 的 SETNX 加锁,只允许一个线程查数据库,其他线程等待重试。
  • 缓存雪崩:大量缓存同时过期,请求全部直击数据库。
    解决方案:

    • 过期时间加随机值(如 30 分钟 ±5 分钟),避免同时过期;
    • 多级缓存:本地缓存(如 Caffeine)+ 分布式缓存(如 Redis),设置不同过期时间。
  1. 线程池调优:提升并发处理能力
    线程池是「并发处理的核心」,合理配置可最大化利用 CPU 和 IO 资源。
  • Tomcat 线程池(处理 HTTP 请求)

    server:
      tomcat:
        threads:
          max: 200 # 最大线程数(默认200)
          min-spare: 10 # 核心线程数(默认10)
        accept-count: 100 # 等待队列长度(默认100)
        connection-timeout: 30000 # 连接超时时间(默认60000ms)
    
     

    配置原则:

    • IO 密集型任务:max-threads 设为 CPU 核心数 ×2+1;
    • CPU 密集型任务:max-threads 设为 CPU 核心数 + 1。
  • @Async 线程池(处理异步任务)
    需自定义线程池(避免默认线程池无上限导致 OOM):

    @Configuration
    @EnableAsync
    public class AsyncConfig {
        @Bean(name = "asyncExecutor")
        public Executor asyncExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(10); // 核心线程数
            executor.setMaxPoolSize(20); // 最大线程数
            executor.setQueueCapacity(1000); // 队列容量
            executor.setKeepAliveSeconds(60); // 线程空闲时间
            executor.setThreadNamePrefix("async-task-"); // 线程名前缀
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
            executor.initialize();
            return executor;
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值