GaussDB DWS 连接池报错排查记录
问题描述
项目从 MySQL 迁移到华为云 GaussDB DWS 后,每次查询必报如下错误:
org.postgresql.util.PSQLException: ERROR: pooler: failed to send set 8 handles.
环境信息:
- Spring Boot 3.x
- dynamic-datasource-spring-boot3-starter
- Druid 连接池
- JDBC 驱动:postgresql 42.x
- 数据库:华为云 GaussDB DWS(通过 PgBouncer 连接池代理)
排查过程
第一阶段:怀疑连接池配置问题
现象: 错误信息 pooler: failed to send set X handles,X 的数字不固定(有时是 1,有时是 8)。
初步判断: PgBouncer 连接池处于 transaction 模式,该模式不允许 JDBC 驱动在建立连接时发送 SET 初始化命令。
尝试的方案:
- 关闭
pool-prepared-statements - 修改
validation-query(去掉FROM DUAL) - 修改 JDBC URL 参数(去掉
useSSL、reWriteBatchedInserts等) - 降级 Druid 版本(从 1.2.27 降到 1.2.16/1.2.21)
结果: 均无效,问题依旧。
第二阶段:确认是客户端问题还是服务端问题
关键发现:
- Navicat 连接正常
- 同事的另一个项目(Druid 1.2.16)连接正常
- 本项目(Druid 1.2.27)每次必报错
结论: 服务端没问题,问题出在客户端。
进一步对比:
| 项目 | Druid 版本 | postgresql 驱动 | 是否正常 |
|---|---|---|---|
| 同事项目 | 1.2.16 | 42.3.8 | ✅ 正常 |
| 本项目 | 1.2.27 | 42.7.8 | ❌ 报错 |
第三阶段:排除连接池影响
测试1:原生 JDBC 连接
java
String url = "jdbc:postgresql://host:port/db?currentSchema=ruoyi-vue-pro";
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1");
// 结果:成功
结论: 原生 JDBC 可以正常连接,说明网络和数据库本身没问题。
测试2:通过连接池执行 SELECT 1
java
// 结果:成功
测试3:通过连接池执行 PreparedStatement
java
PreparedStatement ps = conn.prepareStatement("SELECT COUNT(*) FROM table WHERE col = ?");
ps.setInt(1, 0);
// 结果:失败,报 failed to send set 8 handles
关键结论: SELECT 1(简单查询)成功,PreparedStatement(带参数查询)失败。问题出在 PreparedStatement 的处理上。
第四阶段:换 HikariCP 验证
将 slave 数据源从 Druid 换成 HikariCP:
yaml
slave:
type: com.zaxxer.hikari.HikariDataSource
结果: 同样报 failed to send set 8 handles,排除是 Druid 特有问题。
第五阶段:对比同事项目配置
仔细对比两个项目的配置,发现关键差异:
| 配置项 | 同事项目 | 本项目 |
|---|---|---|
| currentSchema 写法 | currentSchema=test1 |
currentSchema=ruoyi-vue-pro |
| Schema 名称 | 普通名称 | 包含连字符 - |
根本原因
ruoyi-vue-pro 包含连字符 -,在 PostgreSQL/GaussDB 中,schema 名称包含特殊字符时必须用双引号包裹,否则 GaussDB 连接池在解析时会出错,触发 failed to send set handles 异常。
JDBC URL 中 currentSchema=ruoyi-vue-pro 没有加引号,导致 GaussDB 无法正确识别 schema,进而触发连接池报错。
解决方案
在 JDBC URL 中给 schema 名称加上双引号:
yaml
# 修改前(错误)
url: jdbc:postgresql://host:port/twd?currentSchema=ruoyi-vue-pro
# 修改后(正确)
url: jdbc:postgresql://host:port/twd?currentSchema="ruoyi-vue-pro"
# 或者使用 URL 编码
url: jdbc:postgresql://host:port/twd?currentSchema=%22ruoyi-vue-pro%22
完整的推荐配置
yaml
spring:
autoconfigure:
exclude:
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
datasource:
dynamic:
primary: master
datasource:
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://host:port/db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
username: xxx
password: xxx
slave:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://host:port/twd?currentSchema="ruoyi-vue-pro"
username: xxx
password: xxx
druid:
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: true
test-on-return: false
pool-prepared-statements: false
经验总结
-
Schema 名称包含特殊字符(如连字符
-、空格等)时,必须用双引号包裹,否则在 GaussDB/OpenGauss 的连接池代理层会解析失败。 -
排查此类问题的有效方法:
- 先用原生 JDBC
DriverManager.getConnection验证连接是否正常 - 再用简单
SELECT 1和PreparedStatement分别测试,定位问题层级 - 对比能用的项目和不能用的项目的配置差异,重点看 URL 参数
- 先用原生 JDBC
-
使用 dynamic-datasource 多数据源时,必须排除
DruidDataSourceAutoConfigure,否则会导致数据源初始化冲突。 -
GaussDB DWS 连接注意事项:
- Schema 名称含特殊字符必须加双引号
- 不要使用
useSSL=true(连接池代理通常不支持) - 不要开启
pool-prepared-statements validation-query使用SELECT 1,不要加FROM DUAL
相关错误对照表
| 错误信息 | 可能原因 |
|---|---|
pooler: failed to send set X handles |
Schema 名称含特殊字符未加引号;或 PgBouncer 为 transaction 模式 |
Read timed out |
连接空闲超时被服务端断开;或 socketTimeout 设置过小 |
oid type true not known |
Druid 版本与 postgresql 驱动版本不兼容 |
keepAliveBetweenTimeMillis must be greater than timeBetweenEvictionRunsMillis |
keepAlive 时间需大于 timeBetweenEvictionRunsMillis |