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

Java执行长耗时SQL查询时如何添加重复的执行中等待提示日志

实现方案

核心思路是将耗时SQL查询放到独立线程异步执行,同时启动监控逻辑周期性打印提示日志,直到SQL执行完成。

方案1:基础实现(无第三方依赖)

通过Thread+join实现,适合简单场景:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class LongQueryExecutor {
    private static final Logger log = LoggerFactory.getLogger(LongQueryExecutor.class);
    // 日志打印间隔,单位:毫秒,示例为30秒打印一次
    private static final long LOG_INTERVAL = 30 * 1000L;

    public ResultSet execute(Connection conn, String sql) throws InterruptedException {
        final ResultSet[] resultHolder = new ResultSet[1];
        final Exception[] exceptionHolder = new Exception[1];

        // 单独启动线程执行SQL查询
        Thread queryThread = new Thread(() -> {
            try (Statement stmt = conn.createStatement()) {
                resultHolder[0] = stmt.executeQuery(sql);
            } catch (Exception e) {
                exceptionHolder[0] = e;
            }
        });
        queryThread.start();

        // 循环监控SQL线程状态,周期性打印日志
        while (queryThread.isAlive()) {
            log.info("query is executing --- please wait");
            queryThread.join(LOG_INTERVAL);
        }

        // 执行结束后统一处理结果或异常
        if (exceptionHolder[0] != null) {
            throw new RuntimeException("SQL查询执行失败", exceptionHolder[0]);
        }
        return resultHolder[0];
    }
}

注意事项:

  • 数据库连接的超时时间需要设置为大于SQL实际执行时长,避免连接被提前回收
  • ResultSet使用完成后需要按规范关闭,避免资源泄漏
  • 若需要支持查询中断,可在捕获中断信号时调用Statement.cancel()终止数据库端的执行任务

方案2:线程池实现(适合多任务场景)

基于ScheduledExecutorService实现,调度更稳定,适合生产环境:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Future;

public class LongQueryExecutor {
    private static final Logger log = LoggerFactory.getLogger(LongQueryExecutor.class);
    private static final long LOG_INTERVAL = 30L; // 日志间隔,单位:秒
    // 全局共用的调度线程池,核心线程数可按需调整
    private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);

    public ResultSet execute(Connection conn, String sql) throws Exception {
        // 提交定时打印日志的任务
        Runnable logTask = () -> log.info("query is executing --- please wait");
        var logScheduledFuture = SCHEDULER.scheduleAtFixedRate(logTask, 0, LOG_INTERVAL, TimeUnit.SECONDS);

        // 提交SQL查询任务
        Future<ResultSet> queryFuture = SCHEDULER.submit(() -> {
            try (Statement stmt = conn.createStatement()) {
                return stmt.executeQuery(sql);
            }
        });

        try {
            // 阻塞等待查询结果,可传入超时参数控制最大执行时长
            return queryFuture.get();
        } finally {
            // 无论查询成功/失败,都要终止日志打印任务
            logScheduledFuture.cancel(false);
        }
    }
}

可选优化

  • 日志可追加已执行时长,方便业务排查进度
  • 日志打印间隔可抽成配置项,无需硬编码在业务逻辑中

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 15:06:00