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
相关产品推荐
相关产品推荐

