数据库连接池Connection is not available, request timed out after 120000ms

问题描述

系统上线一段时间后,经常会报查:

询数据表总数失败:HikariPool-2 - Connection is not available, request timed out after 120000ms.

系统背景:系统中有定时任务,可能定期需要统计数据库下面表的数据信息

代码

方案1

复制代码
@Component
public class DorisPoolConfig {



    private static final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();

    public static final String DATABASE_PARAM = "?allowPublicKeyRetrieval=true&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false";
    public static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";


    private static final int maxPoolSize = 10;
    private static final int minIdle = 4;
    //2分钟
    private static final long connectionTimeout = 2 * 60 * 1000;

    // 5分钟
    private static final long idleTimeout = 5 * 60 * 1000;
    // 30分钟
    private static final long maxLifetime = 30 * 60 * 1000;


    /**
     * 获取默认引擎连接,不同引擎拥有不同的引擎配置
     *
     * @param engine Doris引擎配置
     * @return Doris连接信息
     */
    public synchronized DataSource getEngineDataSource (Engine engine) {
        return getDataSourceByPrefix(engine, LjPoolEnum.PAGE.getCode());
    }


    /**
     * 获取默认引擎连接,不同引擎拥有不同的引擎配置
     *
     * @param engine Doris引擎配置
     * @return Doris连接信息
     */
    public synchronized DataSource getDataSourceByPrefix (Engine engine,String prefix) {
        String key = prefix + engine.getId();
        //获取数据库类型对应的DataSource
        DataSource dataSource = dataSourceMap.get(key);
        if (Objects.isNull(dataSource)) {
            //获取doris连接信息
            dataSource = getInitDataSource(engine);
            dataSourceMap.put(key, dataSource);
        }
        return dataSource;
    }

    /**
     * 获取Doris引擎连接
     *
     * @param engine Doris引擎配置
     * @return Doris连接信息
     */
    private DataSource getInitDataSource(Engine engine) {
        //配置连接池
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(engine.getUrl() + DATABASE_PARAM);
        config.setDriverClassName(JDBC_DRIVER);
        config.setUsername(engine.getUsername());
        config.setPassword(engine.getPassword());
        config.setMaximumPoolSize(maxPoolSize);
        config.setMinimumIdle(minIdle);
        config.setConnectionTimeout(connectionTimeout);
        config.setIdleTimeout(idleTimeout);
        config.setMaxLifetime(maxLifetime);
        return new HikariDataSource(config);
    }
}
复制代码
调用代码
复制代码
    public long executeQueryCount(DataSource dataSource, String countSql, String exceptionMsg) {
        // 验证SQL是否安全,防止SQL注入
        SqlParserUtil.validateSqlBasic(countSql);
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(countSql);
             ResultSet rs = stmt.executeQuery()) {
            if (rs.next()) {
                return rs.getLong(CommonConstant.ONE);
            }
            return CommonConstant.ZERO;
        } catch (Exception e) {
            exceptionMsg = StringUtils.defaultIfBlank(exceptionMsg, "执行executeQueryCount方法失败");
            log.error(exceptionMsg + ";sql【{}】", countSql);
            throw new CestcBusinessException(HttpCode.BAD_REQUIRED_ERROR,String.format("%s:%s", exceptionMsg,
                    e.getMessage()));
        }
    }
复制代码
分析代码,发现连接池的参数设置有问题,修改后代码

方案2

复制代码
package cn.cestc.servicequality.config;

import cn.cestc.bigdata.common.bean.Engine;
import cn.cestc.servicequality.enums.LjPoolEnum;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 数据库连接池
 *
 */
@Component
public class DorisPoolConfig {



    private static final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();

    public static final String DATABASE_PARAM = "?allowPublicKeyRetrieval=true&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false";
    public static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";
    static final int CONNECT_TIMEOUT_MS = 10 * 1000;
    static final int SOCKET_TIMEOUT_MS = 10 * 60 * 1000;
    static final long LEAK_DETECTION_THRESHOLD = 60 * 1000;


//连接池数量增加,也可以改成nacos动态配置
    private static final int maxPoolSize = 30;
    private static final int minIdle = 4;
    //2分钟
    private static final long connectionTimeout = 2 * 60 * 1000;

    // 5分钟
    private static final long idleTimeout = 5 * 60 * 1000;
    // 30分钟
    private static final long maxLifetime = 30 * 60 * 1000;


    /**
     * 获取默认引擎连接,不同引擎拥有不同的引擎配置
     *
     * @param engine Doris引擎配置
     * @return Doris连接信息
     */
    public synchronized DataSource getEngineDataSource (Engine engine) {
        return getDataSourceByPrefix(engine, LjPoolEnum.PAGE.getCode());
    }


    /**
     * 获取默认引擎连接,不同引擎拥有不同的引擎配置
     *
     * @param engine Doris引擎配置
     * @return Doris连接信息
     */
    public synchronized DataSource getDataSourceByPrefix (Engine engine,String prefix) {
        String key = prefix + engine.getId();
        //获取数据库类型对应的DataSource
        DataSource dataSource = dataSourceMap.get(key);
        if (Objects.isNull(dataSource)) {
            //获取doris连接信息
            dataSource = getInitDataSource(engine, prefix);
            dataSourceMap.put(key, dataSource);
        }
        return dataSource;
    }

    /**
     * 获取Doris引擎连接
     *
     * @param engine Doris引擎配置
     * @return Doris连接信息
     */
    private DataSource getInitDataSource(Engine engine, String prefix) {
        //配置连接池
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(buildJdbcUrl(engine.getUrl()));
        config.setDriverClassName(JDBC_DRIVER);
        config.setUsername(engine.getUsername());
        config.setPassword(engine.getPassword());
        applyPoolSettings(config, engine, prefix);
        return new HikariDataSource(config);
    }

    static String buildJdbcUrl(String jdbcUrl) {
//设置超时时间
        String timeoutParam = "connectTimeout=" + CONNECT_TIMEOUT_MS + "&socketTimeout=" + SOCKET_TIMEOUT_MS;
        if (jdbcUrl.contains("?")) {
            return jdbcUrl + "&" + DATABASE_PARAM.substring(1) + "&" + timeoutParam;
        }
        return jdbcUrl + DATABASE_PARAM + "&" + timeoutParam;
    }

    static void applyPoolSettings(HikariConfig config, Engine engine, String prefix) {
//新增poolName,便于排查问题
        config.setPoolName(prefix + engine.getId());
        config.setMaximumPoolSize(maxPoolSize);
        config.setMinimumIdle(minIdle);
        config.setConnectionTimeout(connectionTimeout);
        config.setIdleTimeout(idleTimeout);
        config.setMaxLifetime(maxLifetime);
        config.setLeakDetectionThreshold(LEAK_DETECTION_THRESHOLD);
       config.setRegisterMbeans(true);
 // 关键:检测死连接
    config.setConnectionTestQuery("SELECT 1");
    config.setValidationTimeout(50000);
    
    // 可选:定期保活(每30秒)
    config.setKeepaliveTime(30000);
    }
}

// 关键:检测死连接

config.setConnectionTestQuery("SELECT 1");

config.setValidationTimeout(5000);

// 可选:定期保活(每30秒)

config.setKeepaliveTime(30000);

方案对比

方面 实现1 实现2 对"找不到连接"的影响
连接超时 10s connect + 600s socket 无显式设置(依赖驱动默认值) 可能改善网络不稳定场景
泄漏检测 60s 有助于发现连接未关闭的bug
JMX监控 开启 关闭 便于运维排查
最大连接数 20 10 并发压力大时可能减少等待
URL参数 动态追加参数 固定拼接 无本质区别
连接池命名 便于日志追踪
相关推荐
z123456789862 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD2 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward3 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记3 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发3 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB3 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
Database_Cool_4 小时前
AI Agent 长期记忆怎么存?阿里云 PolarDB-X 对接 Mem0 记忆框架实践
数据库·阿里云
会编程的土豆4 小时前
数据库范式
数据库·oracle
L1624764 小时前
MySQL 全环境生产快速安装 + 完整配置手册(整合国产银河麒麟适配 + 主从补充版)
数据库·mysql
yio_yin4 小时前
Spring事务管理(数据一致性)
数据库·sql·spring