1 参数 initialSize
连接池初始化时,会调用 com.alibaba.druid.pool.DruidDataSource#init 初始化连接池的数据库连接
while (poolingCount < initialSize) {
try {
// 初始化物理连接
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
connections[poolingCount++] = holder;
} catch (SQLException ex) {
LOG.error("init datasource error, url: " + this.getUrl(), ex);
}
}
2 校验连接
com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker#isValidConnection
public static final String DEFAULT_VALIDATION_QUERY = "SELECT 1";
private Method ping;
private boolean usePingMethod = false;
public MySqlValidConnectionChecker(){
try {
clazz = Utils.loadClass("com.mysql.jdbc.MySQLConnection");
if (clazz == null) {
clazz = Utils.loadClass("com.mysql.cj.jdbc.ConnectionImpl");
}
if (clazz != null) {
ping = clazz.getMethod("pingInternal", boolean.class, int.class);
}
// ping 方法是 mysql-connector-java 包自带的方法,不会为空
if (ping != null) {
usePingMethod = true;
}
} catch (Exception e) {
LOG.warn("Cannot resolve com.mysql.jdbc.Connection.ping method. Will use 'SELECT 1' instead.", e);
}
configFromProperties(System.getProperties());
}
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
// 通过 ping 的方式
if (usePingMethod) {
if (conn instanceof DruidPooledConnection) {
conn = ((DruidPooledConnection) conn).getConnection();
}
if (conn instanceof ConnectionProxy) {
conn = ((ConnectionProxy) conn).getRawObject();
}
if (clazz.isAssignableFrom(conn.getClass())) {
if (validationQueryTimeout <= 0) {
validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
}
try {
ping.invoke(conn, true, validationQueryTimeout * 1000);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
throw e;
}
return true;
}
}
String query = validateQuery;
if (validateQuery == null || validateQuery.isEmpty()) {
query = DEFAULT_VALIDATION_QUERY;
}
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
if (validationQueryTimeout > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
// 通过执行探活 sql
rs = stmt.executeQuery(query);
return true;
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
Druid + MySQL 默认使用 ping 的方式去判断连接是否有效,导致项目配置的 validationQuery 不会执行
解决方案 1:
在进程的启动参数中(jvm参数)设置 -Ddruid.mysql.usePingMethod=false
解放方案 2: 自定义 ValidConnectionChecker
@Slf4j
public class MySqlReplicationValidConnectionChecker extends ValidConnectionCheckerAdapter implements ValidConnectionChecker, Serializable {}
如果项目整合了 shardingJDBC,Druid 执行探活 sql 时,底层不走shardingJDBC。
org.apache.shardingsphere.shardingjdbc.jdbc.core.statement.ShardingStatement 执行sql相关方法时才会走 shardingJDBC 全套流程
3 其他参数列表
配置 | 缺省值 | 说明 |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100。 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
keepAlive | 是否对空闲连接保活。 | |
timeBetweenEvictionRunsMillis | Destroy 线程检测连接的间隔时间,会在检测过程中触发心跳,其中心跳检查会根据配置使用 ping 或 validationQuery 配置的检查语句。 | |
minEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最小时间。 | |
maxEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最长时间。 | |
connectionInitSqls | 物理连接初始化的时候执行的sql。 |