You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中高效执行多个jdbcTemplate查询的最佳方案

下拉筛选预取接口的数据库查询优化方案

现有实现的问题

你当前采用的串行查询逻辑功能上没有问题,但接口总响应时间等于所有查询的耗时累加,且每一次查询都要单独经历数据库连接租用、网络传输、SQL执行的完整流程,当单条查询耗时较高时,接口整体响应速度会受明显影响。

可选的高效查询方案

  • 单SQL合并查询
    所有下拉选项查询都是无关联的只读聚合查询,完全可以通过UNION ALL把多个查询合并为一次数据库请求,把多次网络IO压缩为一次,是性能最高的方案。不同数据库语法略有差异,通用写法示例如下:
    SELECT 'colors' AS filter_key, colors AS filter_value FROM (SELECT DISTINCT colors FROM table1) t1
    UNION ALL
    SELECT 'categories' AS filter_key, categories AS filter_value FROM (SELECT DISTINCT categories FROM table2) t2
    UNION ALL
    SELECT 'weights' AS filter_key, weights AS filter_value FROM (SELECT DISTINCT weights FROM table3) t3;
    
    查询完成后只需要在代码中按filter_key分组,把对应的值拆分到DTO的对应字段即可。这种方案额外开销最低,还能保证所有查询拿到的是同一个时间点的数据快照,不会出现数据不一致的问题。如果单类选项的结果集特别大,需要评估合并后的总数据量是否会带来额外的内存传输压力。
  • 并行执行独立查询
    如果不同查询的逻辑差异大、不适合合并为单条SQL,可以把互相无依赖的查询任务提交到线程池并行执行,最终接口总耗时取决于最慢的单条查询,而非所有查询耗时之和。

是否需要改为异步执行

没有绝对答案,根据实际场景判断即可:

  • 如果所有单条查询耗时都在10ms以内,串行总耗时不超过50ms,完全没必要做异步改造。线程切换、多连接调度的额外开销反而会拉低性能,还会提升代码复杂度。
  • 如果单条查询耗时较高、串行总耗时超过接口响应阈值(比如接口要求200ms内返回,串行执行需要300ms以上),并行改造的收益会非常明显。

注意:并行查询会同时占用多个数据库连接,必须提前评估数据库连接池的容量,避免出现连接不够用、请求排队等待连接的反效果。

Spring Boot 场景下的最佳实践

按照优先级从高到低选择实现方式:

  1. 优先做缓存优化
    下拉选项类数据的变更频率通常极低,优先在Service层加本地缓存(比如Caffeine)或者分布式缓存,设置几分钟到几小时不等的过期时间,直接拦截重复的数据库查询,这是投入产出比最高的优化手段。同时给查询的字段建对应索引,避免distinct查询走全表扫描。
  2. 优先选择SQL合并方案
    简单场景下直接用UNION ALL把多个查询合并为单条请求,代码改动小、性能稳定,没有额外的并发复杂度。
  3. 不适合合并SQL时,用CompletableFuture配合自定义线程池实现并行查询
    不要直接使用@Async注解的默认线程池(无界队列、核心线程数配置不合理容易引发OOM),也不要自己手动new线程,提前配置好和数据库连接池容量匹配的业务线程池,示例实现:
    // 提前在配置类中定义好业务线程池,核心线程数不要超过数据库连接池的最大空闲连接数
    @Resource
    private ThreadPoolExecutor filterQueryThreadPool;
    
    public Filters loadAllFilters(MapSqlParameterSource paramSource) {
        // 提交所有并行查询任务
        CompletableFuture<List<String>> colorTask = CompletableFuture.supplyAsync(
                () -> namedParameterJdbcTemplate.queryForList(getColorsQuery, paramSource, String.class),
                filterQueryThreadPool
        );
        CompletableFuture<List<String>> categoryTask = CompletableFuture.supplyAsync(
                () -> namedParameterJdbcTemplate.queryForList(getCategoriesQuery, paramSource, String.class),
                filterQueryThreadPool
        );
        CompletableFuture<List<String>> weightTask = CompletableFuture.supplyAsync(
                () -> namedParameterJdbcTemplate.queryForList(getWeightsQuery, paramSource, String.class),
                filterQueryThreadPool
        );
    
        // 等待所有任务执行完成后组装结果
        try {
            Filters filters = new Filters();
            filters.setColors(colorTask.get());
            filters.setCategories(categoryTask.get());
            filters.setWeights(weightTask.get());
            return filters;
        } catch (InterruptedException | ExecutionException e) {
            Thread.currentThread().interrupt();
            throw new BusinessException("加载筛选选项失败", e);
        }
    }
    

内容的提问来源于stack exchange,提问作者Gojo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 01:09:20