还在为ShardingSphere-JDBC的“connection closed”错误焦头烂额?别急!这份紧急救援指南将帮你迅速定位并解决问题。
从检查Druid连接池参数(如max-active)到优化maxConnectionSizePerQuery,再到调整MySQL的wait_timeout,每一步都旨在快速恢复系统稳定并显著提升连接效率。立即行动,让你的ShardingSphere-JDBC重焕生机!
在分布式数据库中间件ShardingSphere-JDBC的使用过程中,用户常遇到connection closed错误。
通过分析日志、连接池机制和执行引擎特性,系统性地总结了问题原因及解决方案。
核心原因分析
连接池配置问题
连接泄漏、空闲连接超时和最大连接数不足是常见诱因。
例如,Druid连接池的removeAbandoned配置不当会导致连接被强制回收,而max-active值过小会引发连接不足。
ShardingSphere执行引擎模式
ShardingSphere默认采用MEMORY_STRICTLY模式,每个分片查询使用独立连接。
若maxConnectionSizePerQuery未显式配置,跨分片操作可能因连接数超限而失败。
SQL执行与数据库配置
复杂查询或大数据量操作会导致连接持有时间过长,超过数据库的wait_timeout(如MySQL默认8小时)或连接池的minEvictableIdleTimeMillis,从而被主动关闭。
解决方案
优化连接池配置
以Druid为例,调整以下参数可显著提升稳定性:
lcjmSSL提供免费的SSL证书申请,支持多种类型证书,包括多域名证书、IP证书和通配符证书。用户最多可以在单张证书中申请100个域名,极大地方便了大规模网站的安全部署。无论是普通用户还是企业用户,都可以免费享受这一服务,降低了运维成本。
spring:
shardingsphere:
datasource:
common:
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
test-while-idle: true
代码解释:
initial-size和min-idle保证基础连接数,避免频繁创建/销毁。max-active限制最大连接数,防止资源耗尽。min-evictable-idle-time-millis与数据库wait_timeout同步,避免超时断开。
调整ShardingSphere执行引擎
显式配置maxConnectionSizePerQuery,控制单次查询的并发连接数:
spring:
shardingsphere:
props:
max-connection-size-per-query: 10
关键点:
- 该值需根据分片数调整,避免过大导致连接池耗尽。
- OLTP场景建议使用
CONNECTION_STRICTLY模式(通过maxConnectionSizePerQuery=1隐式触发)。
数据库与SQL优化
-
调整MySQL超时参数:
SET GLOBAL wait_timeout = 28800; -- 8小时(单位:秒) -
优化SQL与分片策略:
- 确保查询包含分片键,减少跨分片操作。
- 对大结果集分页查询,避免单次拉取过多数据。
监控与日志
启用ShardingSphere的SQL日志,定位慢查询:
spring:
shardingsphere:
props:
sql-show: true
通过Druid监控页面或JMX实时观察连接池状态,及时发现异常。
关键配置总结
配置项
推荐值(根据场景调整)
作用
maxConnectionSizePerQuery
分片数或略高(如10)
控制单次查询的并发连接数
initial-size/max-active
5-20(根据负载)
连接池初始/最大连接数
wait_timeout(MySQL)
与连接池minEvictableIdleTimeMillis一致
防止数据库主动断开连接
验证示例
以下代码模拟高并发查询,验证连接池稳定性:
import org.apache.shardingsphere.driver.jdbc.core.datasource.ShardingSphereDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConnectionPoolTest {
public static void main(String[] args) throws Exception {
DataSource dataSource = getShardingSphereDataSource(); // 替换为实际数据源
ExecutorService executor = Executors.newFixedThreadPool(20);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM eb_order_blind_box_profit_pool LIMIT 1")) {
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getString("id"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
private static DataSource getShardingSphereDataSource() {
// 返回实际的ShardingSphereDataSource实例
return null;
}
}