国产数据库连接池配置踩坑记:达梦、GaussDB、人大金仓的最佳实践对比

背景:信创迁移中的"隐形地雷"

信创替代项目推进过程中,很多团队把精力放在了 SQL 兼容性、存储过程改写、数据类型映射这些显性问题上。但真正上线后,最先暴露的性能问题往往来自一个不起眼的环节——数据库连接池配置

国产数据库虽然大多兼容 JDBC 协议,但在连接生命周期管理、心跳检测机制、超时行为等方面与 MySQL/Oracle 存在显著差异。直接沿用原有连接池参数,轻则出现连接泄漏,重则引发大面积服务不可用。

本文基于某大型政企项目的实际迁移经验,对比 达梦 DM8华为 GaussDB人大金仓 KingbaseES 三款主流国产数据库在连接池层面的表现,给出可落地的配置建议。

测试环境说明

| 项目 | 规格 |

|------|------|

| 应用服务器 | 8C16G × 3 台(Spring Boot 2.7 + JDK 17) |

| 数据库服务器 | 16C64G(单节点部署) |

| 连接池组件 | HikariCP 4.0.3 / Druid 1.2.16 |

| 压测工具 | JMeter 5.5 + 自定义 Java 客户端 |

| 网络环境 | 内网千兆,平均延迟 0.3ms |

HikariCP vs Druid:国产数据库适配差异

在讨论具体数据库之前,先明确一个前提:连接池选型本身就会影响最终表现

HikariCP 的优劣势

HikariCP 以轻量、高性能著称,是 Spring Boot 的默认连接池。但其设计理念是"少配置、快执行",对数据库端的异常状态感知相对粗糙。


# HikariCP 基础配置(Spring Boot)
spring:
  datasource:
    hikari:
      minimum-idle: 10
      maximum-pool-size: 50
      idle-timeout: 600000       # 10 分钟
      max-lifetime: 1800000      # 30 分钟
      connection-timeout: 30000  # 30 秒
      connection-test-query: SELECT 1

核心问题在于:HikariCP 的 connection-test-query 只在获取连接时执行,空闲连接的存活性依赖 idle-timeoutmax-lifetime 的被动淘汰。如果数据库端主动断开连接(如防火墙策略、数据库重启),HikariCP 可能持有大量"死连接"。

Druid 的优劣势

Druid 在连接保活和监控方面功能更丰富,但配置项繁多,调优门槛更高。


# Druid 基础配置
spring:
  datasource:
    druid:
      initial-size: 10
      min-idle: 10
      max-active: 50
      max-wait: 30000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      keep-alive: true           # 关键参数
      keep-alive-between-time-millis: 60000

Druid 的 keep-alive 机制会主动向空闲连接发送心跳探测,这对国产数据库来说是个加分项——因为国产数据库的服务端连接超时行为往往不如 MySQL 那样可预期。

选型建议速查

| 维度 | HikariCP | Druid |

|------|----------|-------|

| 吞吐量(高并发) | ★★★★★ | ★★★★ |

| 连接保活能力 | ★★★ | ★★★★★ |

| 监控能力 | ★★ | ★★★★★ |

| 配置复杂度 | 低 | 高 |

| 国产数据库适配 | 需额外调优 | 相对友好 |

| SQL 防火墙 | 无 | 内置 |

结论:信创项目建议优先选 Druid,尤其是生产环境网络策略复杂(有防火墙、负载均衡)的场景。

达梦 DM8 连接池配置

JDBC 驱动特性

达梦的 JDBC 驱动(DmJdbcDriver18.jar)整体兼容性较好,但有几个容易踩坑的点:

1. 驱动类名dm.jdbc.driver.DmDriver(注意大小写)

2. URL 格式jdbc:dm://host:5236/dbname(默认端口 5236)

3. 连接验证语句SELECT 1 可用,但推荐用 SELECT 1 FROM DUAL


# 达梦 DM8 + HikariCP 推荐配置
spring:
  datasource:
    driver-class-name: dm.jdbc.driver.DmDriver
    url: jdbc:dm://192.168.1.100:5236?schema=PROD_DB
    username: SYSDBA
    password: ${DM_PASSWORD}
    hikari:
      minimum-idle: 20
      maximum-pool-size: 80
      idle-timeout: 300000       # 5 分钟(达梦默认空闲超时较短)
      max-lifetime: 900000       # 15 分钟
      connection-timeout: 15000
      connection-test-query: SELECT 1 FROM DUAL
      # 达梦驱动对 keepalive 的支持有限,需要靠连接池主动探测
      keepalive-time: 120000     # 2 分钟发一次保活

达梦的特殊行为

坑 1:会话空闲超时

达梦 DM8 有一个隐含参数 SESS_IDLE_TIMEOUT(默认 0 表示不限制),但在很多部署环境中,DBA 会将其设置为 300 秒(5 分钟)。如果连接池的 idle-timeout 大于这个值,连接会被数据库端主动断开。


-- 查看当前会话空闲超时设置
SELECT PARA_NAME, PARA_VALUE 
FROM V$DM_INI 
WHERE PARA_NAME = 'SESS_IDLE_TIMEOUT';

-- 建议设置为 0 或与连接池 idle-timeout 协调
ALTER SYSTEM SET 'SESS_IDLE_TIMEOUT' = 0;

坑 2:连接池初始化慢

达梦的 JDBC 连接建立过程比 MySQL 慢 2-3 倍(涉及 License 验证、字符集协商等),首次启动时如果 initial-size 设置过大,可能导致应用启动超时。


连接建立耗时对比(毫秒):
MySQL 5.7:    avg=8ms   p99=25ms
达梦 DM8:     avg=22ms  p99=85ms
GaussDB:      avg=15ms  p99=45ms
KingbaseES:   avg=12ms  p99=35ms

坑 3:Prepared Statement 缓存

达梦驱动对 PreparedStatement 的缓存机制与 Oracle 不同,如果连接池开启了 cachePrepStmts,可能出现内存泄漏。建议在达梦环境中关闭此选项。

Druid 推荐配置


spring:
  datasource:
    druid:
      # 达梦专用配置
      initial-size: 10
      min-idle: 20
      max-active: 80
      max-wait: 15000
      validation-query: SELECT 1 FROM DUAL
      validation-query-timeout: 5
      test-while-idle: true
      test-on-borrow: true       # 达梦环境建议开启
      test-on-return: false
      time-between-eviction-runs-millis: 30000
      min-evictable-idle-time-millis: 180000
      keep-alive: true
      keep-alive-between-time-millis: 45000
      phy-timeout-millis: 900000
      # 关闭 PSCache(达梦不兼容)
      pool-prepared-statements: false

华为 GaussDB 连接池配置

JDBC 驱动特性

GaussDB(此处指 GaussDB for MySQL 和 GaussDB 主备版)的驱动情况比较复杂:

  • **GaussDB for MySQL**:直接使用 MySQL Connector/J,配置与 MySQL 基本一致
  • **GaussDB 主备版(openGauss)**:使用 `org.opengauss.Driver`,端口默认 5432
  • **GaussDB 分布式版**:需要配置 CN(Coordinator Node)地址
  • 本文以 GaussDB 主备版 为主进行说明。

    
    # GaussDB 主备版 + HikariCP 推荐配置
    spring:
      datasource:
        driver-class-name: org.opengauss.Driver
        url: jdbc:opengauss://192.168.1.200:5432/prod_db?currentSchema=public
        username: gaussdb_user
        password: ${GAUSS_PASSWORD}
        hikari:
          minimum-idle: 15
          maximum-pool-size: 60
          idle-timeout: 300000
          max-lifetime: 1200000      # 20 分钟
          connection-timeout: 20000
          connection-test-query: SELECT 1
          # GaussDB 支持 sslmode,生产环境建议开启
          data-source-properties:
            sslmode: require
            socketTimeout: 60000
    

    GaussDB 的特殊行为

    坑 1:SSL 连接开销

    GaussDB 默认强制 SSL 连接(sslmode=require),这会带来额外的握手开销。在连接池初始化阶段影响明显:

    
    SSL vs 非 SSL 连接建立耗时:
    非 SSL:  avg=12ms  p99=35ms
    SSL:     avg=28ms  p99=95ms
    SSL+证书验证: avg=45ms  p99=150ms
    

    建议:生产环境使用 SSL 但跳过证书验证(sslmode=require + sslfactory=org.opengauss.ssl.NonValidatingFactory),内网环境下安全性可接受。

    坑 2:连接空闲回收

    GaussDB 服务端有一个 authentication_timeout 参数(默认 60 秒),这个参数影响的不仅是认证阶段。如果连接在认证后长时间没有发送 SQL,某些版本的 GaussDB 会将其视为"僵尸连接"并回收。

    
    -- 查看 GaussDB 连接相关参数
    SHOW authentication_timeout;
    SHOW idle_in_transaction_session_timeout;
    
    -- 建议设置(需 DBA 操作)
    ALTER SYSTEM SET idle_in_transaction_session_timeout = '30min';
    

    坑 3:主备切换时的连接处理

    GaussDB 主备切换时,已有连接不会自动重定向到新的主节点。连接池需要能够感知这种变化:

    
    # Druid 针对 GaussDB 主备场景的配置
    spring:
      datasource:
        druid:
          # 开启连接异常自动重连
          break-after-acquire-failure: false
          connection-error-retry-attempts: 3
          # 主备切换后,快速淘汰旧连接
          test-while-idle: true
          time-between-eviction-runs-millis: 15000  # 缩短探测间隔
    

    压测数据

    在 200 并发线程持续压测 30 分钟的场景下:

    | 指标 | HikariCP | Druid |

    |------|----------|-------|

    | TPS(简单查询) | 12,850 | 11,200 |

    | TPS(复杂事务) | 3,400 | 3,250 |

    | P99 延迟(ms) | 45 | 52 |

    | 连接泄漏次数 | 2 | 0 |

    | 主备切换恢复时间 | 8s | 3s |

    人大金仓 KingbaseES 连接池配置

    JDBC 驱动特性

    KingbaseES 基于 PostgreSQL 开发,其 JDBC 驱动(kingbase8-8.6.0.jar)与 PostgreSQL 驱动高度兼容。

    
    # KingbaseES + HikariCP 推荐配置
    spring:
      datasource:
        driver-class-name: com.kingbase8.Driver
        url: jdbc:kingbase8://192.168.1.300:54321/prod_db
        username: system
        password: ${KB_PASSWORD}
        hikari:
          minimum-idle: 15
          maximum-pool-size: 60
          idle-timeout: 600000       # 10 分钟
          max-lifetime: 1800000      # 30 分钟
          connection-timeout: 20000
          connection-test-query: SELECT 1
          data-source-properties:
            socketTimeout: 60000
            tcpKeepAlive: true
    

    KingbaseES 的特殊行为

    坑 1:端口冲突

    KingbaseES 默认端口是 54321(注意不是 PostgreSQL 的 5432),但很多部署时会改为 5432。连接 URL 中端口写错是最常见的低级错误。

    坑 2:Schema 搜索路径

    KingbaseES 的默认 Schema 行为与 PostgreSQL 一致,但在信创迁移场景中,很多表是从 Oracle 迁过来的,Schema 名称可能不同:

    
    # 在 URL 中指定 searchpath
    url: jdbc:kingbase8://host:54321/db?currentSchema=app_schema&searchpath=app_schema,public
    

    坑 3:大结果集内存溢出

    KingbaseES 驱动在默认情况下会将整个结果集加载到内存中。如果查询返回大量数据,需要显式设置 FetchSize:

    
    // 正确的流式读取方式
    PreparedStatement stmt = conn.prepareStatement(
        "SELECT * FROM big_table WHERE status = ?",
        ResultSet.TYPE_FORWARD_ONLY,
        ResultSet.CONCUR_READ_ONLY
    );
    stmt.setFetchSize(1000);  // 每次从服务端取 1000 行
    stmt.setString(1, "ACTIVE");
    ResultSet rs = stmt.executeQuery();
    while (rs.next()) {
        // 处理数据
    }
    

    坑 4:连接泄漏检测

    KingbaseES 没有像 MySQL SHOW PROCESSLIST 那样的便捷命令,排查连接泄漏需要查系统视图:

    
    -- 查看当前活跃连接
    SELECT pid, usename, application_name, client_addr, 
           state, query_start, state_change
    FROM sys_stat_activity
    WHERE state != 'idle'
    ORDER BY query_start;
    
    -- 查看连接数分布
    SELECT usename, count(*) as conn_count, 
           count(CASE WHEN state = 'active' THEN 1 END) as active_count
    FROM sys_stat_activity
    GROUP BY usename;
    

    超时控制差异对比

    超时控制是连接池配置中最容易出问题的部分。三款数据库在超时行为上有明显差异:

    
    连接生命周期时序图:
    
      [应用]              [连接池]              [数据库]
        |                    |                     |
        |-- getConnection -->|                     |
        |                    |-- 创建连接 -------->|
        |                    |<-- 返回连接 --------|
        |<-- 获得连接 -------|                     |
        |                    |                     |
        |   ... 业务处理 ...                       |
        |                    |                     |
        |   ... 空闲等待 ...                       |
        |                    |                     |
        |                    |   [数据库主动断开]   |
        |                    |<--- 连接中断 -------|  ← 这里最容易出问题
        |                    |                     |
        |-- getConnection -->|                     |
        |                    |-- 复用旧连接 ??     |
        |                    |    (失败!)          |
    

    各数据库超时参数对照

    | 超时类型 | 达梦 DM8 | GaussDB | KingbaseES |

    |---------|---------|---------|-----------|

    | 连接建立超时 | loginTimeout (URL) | loginTimeout (URL) | loginTimeout (URL) |

    | SQL 执行超时 | queryTimeout (JDBC) | queryTimeout (JDBC) | queryTimeout (JDBC) |

    | 空闲会话超时 | SESS_IDLE_TIMEOUT | idle_in_transaction_session_timeout | idle_session_timeout |

    | 事务超时 | 无内置限制 | statement_timeout | statement_timeout |

    | TCP KeepAlive | 支持(OS 层面) | 支持(OS 层面) | 支持(tcpKeepAlive 参数) |

    | 服务端主动断开通知 | 无 | 有(部分版本) | 有 |

    推荐的超时配置组合

    
    # 通用超时策略(适配三款数据库)
    # 原则:连接池超时 < 数据库服务端超时 < 防火墙超时
    
    connection-pool:
      connection-timeout: 15000      # 连接获取超时 15s
      idle-timeout: 300000           # 空闲连接淘汰 5min
      max-lifetime: 900000           # 连接最大生命周期 15min
      keepalive-time: 120000         # 保活探测间隔 2min
    
    database-server:
      idle-session-timeout: 1800     # 服务端空闲超时 30min
      tcp-keepalive: 60              # TCP 保活间隔 60s
    
    firewall:
      session-timeout: 3600          # 防火墙会话超时 1h
    

    连接泄漏检测实战

    连接泄漏是生产环境中最棘手的问题之一。以下是针对三款数据库的检测方案:

    通用检测策略

    
    // HikariCP 连接泄漏检测配置
    HikariConfig config = new HikariConfig();
    config.setLeakDetectionThreshold(60000); // 连接借用超过 60s 即告警
    
    // Druid 连接泄漏检测
    // 通过 removeAbandoned 机制
    druid.removeAbandoned=true
    druid.removeAbandonedTimeout=300  // 5分钟
    druid.logAbandoned=true           // 记录堆栈
    

    达梦 DM8 泄漏排查

    
    -- 达梦查看长时间未释放的连接
    SELECT SESS_ID, USER_NAME, CLNT_IP, CLNT_PORT,
           LAST_SEND_TIME, STATE, SQL_TEXT
    FROM V$SESSIONS
    WHERE STATE = 'ACTIVE'
      AND DATEDIFF(SS, LAST_SEND_TIME, SYSDATE) > 300
    ORDER BY LAST_SEND_TIME;
    

    GaussDB 泄漏排查

    
    -- GaussDB 查看活跃会话
    SELECT pid, usename, client_addr, state,
           now() - query_start AS duration,
           query
    FROM pg_stat_activity
    WHERE state != 'idle'
      AND now() - query_start > interval '5 minutes'
    ORDER BY duration DESC;
    
    -- 强制终止泄漏连接
    SELECT pg_terminate_backend(pid)
    FROM pg_stat_activity
    WHERE state = 'idle in transaction'
      AND now() - state_change > interval '10 minutes';
    

    KingbaseES 泄漏排查

    
    -- KingbaseES 查看连接详情(语法与 PG 类似但视图名不同)
    SELECT pid, usename, client_addr, state,
           now() - query_start AS duration,
           left(query, 100) AS query_preview
    FROM sys_stat_activity
    WHERE state != 'idle'
      AND now() - query_start > interval '5 minutes'
    ORDER BY duration DESC;
    

    性能压测数据汇总

    测试场景

  • **场景 A**:简单 CRUD,200 并发线程,持续 30 分钟
  • **场景 B**:混合事务(含批量插入),100 并发线程,持续 30 分钟
  • **场景 C**:长连接空闲后恢复(模拟网络抖动),50 并发
  • 场景 A 结果

    | 数据库 | 连接池 | TPS | P50 (ms) | P99 (ms) | 错误率 |

    |--------|-------|-----|----------|----------|--------|

    | 达梦 DM8 | HikariCP | 8,520 | 12 | 48 | 0.01% |

    | 达梦 DM8 | Druid | 8,180 | 13 | 52 | 0.00% |

    | GaussDB | HikariCP | 11,300 | 8 | 32 | 0.02% |

    | GaussDB | Druid | 10,850 | 9 | 38 | 0.00% |

    | KingbaseES | HikariCP | 9,750 | 10 | 40 | 0.01% |

    | KingbaseES | Druid | 9,400 | 11 | 42 | 0.00% |

    场景 C 结果(网络抖动恢复)

    | 数据库 | 连接池 | 恢复时间 | 恢复期间错误数 | 连接池自动恢复 |

    |--------|-------|---------|--------------|--------------|

    | 达梦 DM8 | HikariCP | 45s | 128 | 需手动干预 |

    | 达梦 DM8 | Druid | 12s | 23 | 自动恢复 |

    | GaussDB | HikariCP | 35s | 89 | 部分自动 |

    | GaussDB | Druid | 8s | 12 | 自动恢复 |

    | KingbaseES | HikariCP | 28s | 67 | 自动恢复 |

    | KingbaseES | Druid | 10s | 18 | 自动恢复 |

    生产环境配置模板

    综合以上分析,给出三款数据库的生产环境推荐配置:

    达梦 DM8 生产配置

    
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: dm.jdbc.driver.DmDriver
        url: jdbc:dm://${DM_HOST}:${DM_PORT}?schema=${DM_SCHEMA}&loginTimeout=10000
        druid:
          initial-size: 5
          min-idle: 20
          max-active: 100
          max-wait: 15000
          validation-query: SELECT 1 FROM DUAL
          validation-query-timeout: 5
          test-while-idle: true
          test-on-borrow: true
          test-on-return: false
          time-between-eviction-runs-millis: 20000
          min-evictable-idle-time-millis: 180000
          keep-alive: true
          keep-alive-between-time-millis: 45000
          pool-prepared-statements: false
          remove-abandoned: true
          remove-abandoned-timeout: 300
          log-abandoned: true
          # 监控配置
          filters: stat,wall
          stat-view-servlet:
            enabled: true
            url-pattern: /druid/*
    

    GaussDB 生产配置

    
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.opengauss.Driver
        url: jdbc:opengauss://${GAUSS_HOST}:${GAUSS_PORT}/${GAUSS_DB}?currentSchema=public&sslmode=require&sslfactory=org.opengauss.ssl.NonValidatingFactory&socketTimeout=60000
        druid:
          initial-size: 5
          min-idle: 15
          max-active: 80
          max-wait: 15000
          validation-query: SELECT 1
          validation-query-timeout: 5
          test-while-idle: true
          test-on-borrow: false
          test-on-return: false
          time-between-eviction-runs-millis: 15000
          min-evictable-idle-time-millis: 300000
          keep-alive: true
          keep-alive-between-time-millis: 60000
          pool-prepared-statements: true
          max-pool-prepared-statement-per-connection-size: 20
          remove-abandoned: true
          remove-abandoned-timeout: 300
          log-abandoned: true
    

    KingbaseES 生产配置

    
    spring:
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.kingbase8.Driver
        url: jdbc:kingbase8://${KB_HOST}:${KB_PORT}/${KB_DB}?currentSchema=${KB_SCHEMA}&tcpKeepAlive=true&socketTimeout=60000
        druid:
          initial-size: 5
          min-idle: 15
          max-active: 80
          max-wait: 15000
          validation-query: SELECT 1
          validation-query-timeout: 5
          test-while-idle: true
          test-on-borrow: false
          test-on-return: false
          time-between-eviction-runs-millis: 30000
          min-evictable-idle-time-millis: 300000
          keep-alive: true
          keep-alive-between-time-millis: 60000
          pool-prepared-statements: true
          max-pool-prepared-statement-per-connection-size: 20
          remove-abandoned: true
          remove-abandoned-timeout: 300
          log-abandoned: true
    

    配置检查清单

    上线前逐项核对:

    
    □ 连接池最大连接数 ≤ 数据库 max_connections 的 80%
    □ idle-timeout < 数据库 idle_session_timeout
    □ max-lifetime < 防火墙 session-timeout
    □ keep-alive 间隔 < 所有超时参数中的最小值
    □ validation-query 在目标数据库上执行通过
    □ 连接泄漏检测已开启(removeAbandoned 或 leakDetection)
    □ 监控指标已接入 Prometheus/Grafana
    □ 主备切换场景已做演练测试
    □ 应用启动超时已考虑连接初始化耗时
    □ SSL 配置已验证(如适用)
    

    信创迁移是一场持久战,连接池配置只是其中一环,但往往是最先被忽视的一环。希望这篇文章能帮你在项目初期就避开这些坑。


    原文链接:https://wenyiblog.top/2026/06/domestic-db-connection-pool-guide/

    首发于文艺技术笔记(wenyiblog.top),转载请注明出处。

    posted @ 2026-06-22 19:31  软件工程师文艺  阅读(1)  评论(0)    收藏  举报