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

Spring Boot异步控制器配置后仍超时:80K+数据查询遇问题

解决Spring Boot异步接口超时+大量数据返回问题

嘿,我看了你遇到的问题——配置了异步但还是超时,核心问题其实出在异步没真正生效和单次返回80K+数据的性能瓶颈上,咱们一步步拆解解决:

1. 先把异步的坑填上:Controller里的阻塞调用是白忙活

你当前Controller里的CompletableFuture.allOf(positionList).join()和positionList.get()完全是画蛇添足!这俩方法会让处理请求的主线程一直等着异步任务做完,等于异步配置白搭了。Spring MVC本身支持返回CompletableFuture,会自动帮你释放主线程,等异步任务完成后再给客户端响应,根本不需要手动阻塞。

改完的Controller代码应该是这样:

@RequestMapping(value = "/v1/lr/fullpositionasync", produces = {APPLICATION_JSON_UTF8_VALUE}, method = RequestMethod.GET)
@ResponseBody
public CompletableFuture<List<Position>> retrieveTradePositionsFullAsync(HttpServletRequest request, HttpServletResponse response) {
    try {
        // 直接返回Service的异步任务就行,不用额外处理
        return positionService.getFullPosition();
    } catch(Exception e){
        log.info("Error Occurred in Controller is:"+e.getMessage());
        // 这里要返回失败的CompletableFuture,不然容器会一直挂着等结果
        return CompletableFuture.failedFuture(e);
    }
}

2. 治标又治本:解决80K数据返回的性能问题

说实话,单次返回80K条记录,不管异步与否,序列化JSON、网络传输都会耗很久,这才是超时的根源。有两个靠谱的优化方向:

方向一:分页查询(最推荐)

让客户端传pageNum和pageSize参数,后端只返回当前页的数据,比如每页1000条,80K数据分80次请求,每次响应时间能压到几十毫秒级。

方向二:流式返回(如果必须一次性给全量数据)

用Spring的StreamingResponseBody把数据流式输出,不用把所有数据加载到内存里,同时客户端能边接收边处理,减少等待时间。示例代码如下:

Controller层:

@RequestMapping(value = "/v1/lr/fullpositionstream", produces = {APPLICATION_JSON_UTF8_VALUE}, method = RequestMethod.GET)
public StreamingResponseBody retrieveTradePositionsStream(HttpServletRequest request, HttpServletResponse response) {
    response.setContentType("application/json; charset=UTF-8");
    return outputStream -> {
        ObjectMapper objectMapper = new ObjectMapper();
        // 这里建议改成流式查询的DAO方法,避免一次性加载80K数据占满内存
        Stream<Position> positionStream = positionDao.retrieveDataStream();
        
        outputStream.write("[".getBytes(StandardCharsets.UTF_8));
        Iterator<Position> iterator = positionStream.iterator();
        boolean first = true;
        while (iterator.hasNext()) {
            if (!first) {
                outputStream.write(",".getBytes(StandardCharsets.UTF_8));
            }
            first = false;
            objectMapper.writeValue(outputStream, iterator.next());
        }
        outputStream.write("]".getBytes(StandardCharsets.UTF_8));
    };
}

DAO层改成流式查询(避免内存爆炸):

public Stream<Position> retrieveDataStream() {
    return jdbcTemplate.query(ALL_POSITION_QUERY, rs -> {
        List<Position> batch = new ArrayList<>(100);
        do {
            Position position = new Position();
            // 手动映射字段,或者复用你的BeanPropertyRowMapper逻辑
            position.setId(rs.getLong("id"));
            // ...其他字段映射
            batch.add(position);
            // 每100条输出一批,控制内存占用
            if (batch.size() == 100) {
                Stream<Position> stream = batch.stream();
                batch.clear();
                return stream;
            }
        } while (rs.next());
        return batch.stream();
    }).flatMap(Function.identity());
}

3. 优化线程池配置:别搞1000个线程吓数据库

你现在的线程池setMaxPoolSize(1000)太夸张了!这么多线程同时去抢数据库连接,会直接把数据库连接池耗干,反而导致更多超时。建议根据数据库连接池的大小来配置:

@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    // 核心线程数建议设为CPU核心数*2,比如8核CPU就设16
    executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
    // 最大线程数别超过数据库连接池的配置(比如数据库连接池设的是20,这里就设20)
    executor.setMaxPoolSize(20);
    // 加个任务队列,避免一下子涌进来的请求把线程池撑爆
    executor.setQueueCapacity(100);
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setThreadNamePrefix("AsynchThreadForEndPoint-");
    executor.initialize();
    log.info("Executor is :"+executor.toString());
    return executor;
}

4. 别忘了查数据库本身的性能

最后,存储过程AllPositionProcedure()返回80K数据的速度怎么样?可以去数据库里单独执行这个存储过程看看耗时:

  • 检查存储过程里的SQL有没有加索引
  • 看看能不能只返回客户端需要的字段,减少数据量
  • 考虑把存储过程改成支持分页的版本

总结一下:异步只是释放了Web容器的线程,但解决超时的核心还是要减少单次请求的数据量,优化查询和传输性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:44:26