Druid是一个JDBC组件,它包括三部分:
- DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系。
- DruidDataSource 高效可管理的数据库连接池。
- SQLParser
常见配置说明
name
|
配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
|
|
jdbcUrl
|
连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
|
|
username
|
连接数据库的用户名
|
|
password
|
连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
|
|
driverClassName
|
根据url自动识别
|
这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName
|
initialSize
|
0
|
初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
|
maxActive
|
8
|
连接池中最大连接数量
|
maxIdle
|
8
|
连接池中最大连接数量,已经不再使用,配置了也没效果
|
minIdle
|
0
|
连接池中最小连接数量
|
maxWait
|
-1
|
获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
|
poolPreparedStatements
|
false
|
是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。5.5及以上版本有PSCache,建议开启。
|
maxOpenPreparedStatements
|
-1
|
要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
|
maxPoolPreparedStatementsPerConnectionSize
|
10
|
为每个连接缓存的preparedStatement的最大数量
|
validationQuery
|
null
|
用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用
|
validationQueryTimeout
|
-1
|
执行validationQuery的超时时间
|
testOnBorrow
|
false
|
申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
|
testOnReturn
|
false
|
归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
|
testWhileIdle
|
true
|
建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
|
timeBetweenEvictionRunsMillis
|
-1L(最新版本1.0.17中已改为60 * 1000L)
|
有两个含义:
1) Destroy线程检测连接的间隔时间,此时如果未配置,则间隔1000毫秒 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 |
minEvictableIdleTimeMillis
|
1000L * 60L * 30L
|
空闲连接回收的最小空闲时间
|
numTestsPerEvictionRun
|
3
|
不再使用,一个DruidDataSource只支持一个EvictionRun
|
connectionInitSqls
|
物理连接初始化的时候执行的sql
|
|
exceptionSorter
|
null,根据dbType自动识别
|
当数据库抛出一些不可恢复的异常时,抛弃连接
|
filters
|
属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall |
|
proxyFilters
|
类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
|
|
removeAbandoned
|
false
|
是否强制关闭连接时长大于removeAbandonedTimeoutMillis的连接
|
removeAbandonedTimeoutMillis
|
300 * 1000
|
一个连接从被连接到被关闭之间的最大生命周期
|
logAbandoned
|
false
|
强制关闭连接时是否记录日志
|
queryTimeout
|
执行查询的超时时间(秒),执行Statement对象时如果超过此时间,则抛出SQLException
|
|
transactionQueryTimeout
|
执行一个事务的超时时间(秒),执行Statement对象 时判断是否为事务,如果是且此项未设置,则使用queryTimeout
|
部分配置项生效过程理解
配置项中指定了各个参数后,在连接池内部是这么使用这些参数的。数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,会从 池中取出一个连接。如果当前池中正在使用的连接数等于maxActive,则会等待一段时间,等待其他操作释放掉某一个连接,如果这个等待时间超过了 maxWait,则会报错;如果当前正在使用的连接数没有达到maxActive,则判断当前是否有空闲连接,如果有则直接使用空闲连接,如果没有则新建 立一个连接。在连接使用完毕后,不是将其物理连接关闭,而是将其放入池中等待其他操作复用。
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
init();
if (filters.size() > 0) {
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {
return getConnectionDirect(maxWaitMillis);
}
}
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
int notFullTimeoutRetryCnt = 0;
for (;;) {
// handle notFullTimeoutRetry
DruidPooledConnection poolableConnection;
try {
poolableConnection = getConnectionInternal(maxWaitMillis);
} catch (GetConnectionTimeoutException ex) {
if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
notFullTimeoutRetryCnt++;
if (LOG.isWarnEnabled()) {
LOG.warn("not full timeout retry : " + notFullTimeoutRetryCnt);
}
continue;
}
throw ex;
}
if (isTestOnBorrow()) {
boolean validate = testConnectionInternal(poolableConnection.getConnection());
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
Connection realConnection = poolableConnection.getConnection();
discardConnection(realConnection);
continue;
}
} else {
Connection realConnection = poolableConnection.getConnection();
if (realConnection.isClosed()) {
discardConnection(null); // 传入null,避免重复关闭
continue;
}
if (isTestWhileIdle()) {
final long currentTimeMillis = System.currentTimeMillis();
final long lastActiveTimeMillis = poolableConnection.getConnectionHolder().getLastActiveTimeMillis();
final long idleMillis = currentTimeMillis - lastActiveTimeMillis;
long timeBetweenEvictionRunsMillis = this.getTimeBetweenEvictionRunsMillis();
if (timeBetweenEvictionRunsMillis <= 0) {
timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
}
if (idleMillis >= timeBetweenEvictionRunsMillis) {
boolean validate = testConnectionInternal(poolableConnection.getConnection());
if (!validate) {
if (LOG.isDebugEnabled()) {
LOG.debug("skip not validate connection.");
}
discardConnection(realConnection);
continue;
}
}
}
}
……
return poolableConnection;
}
}
同时连接池内部有机制判断,如果当前的总的连接数少于minIdle,则会建立新的空闲连接,以保证连接数达到minIdle。以上源码可以看出, 当testWhileIdle参数为true时,在获取连接时将对得到的连接进行检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,则执行validationQuery验证连接是否有效。
public class DestroyConnectionThread extends Thread {
public DestroyConnectionThread(String name){
super(name);
this.setDaemon(true);
}
public void run() {
initedLatch.countDown();
for (;;) {
// 从前面开始删除
try {
if (closed) {
break;
}
if (timeBetweenEvictionRunsMillis > 0) {
Thread.sleep(timeBetweenEvictionRunsMillis);
} else {
Thread.sleep(1000); //
}
if (Thread.interrupted()) {
break;
}
destroyTask.run();
} catch (InterruptedException e) {
break;
}
}
}
}
public class DestroyTask implements Runnable {
@Override
public void run() {
shrink(true);
if (isRemoveAbandoned()) {
removeAbandoned();
}
}
}
public void shrink(boolean checkTime) {
final List evictList = new ArrayList();
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
return;
}
try {
final int checkCount = poolingCount - minIdle;
final long currentTimeMillis = System.currentTimeMillis();
for (int i = 0; i < checkCount; ++i) {
DruidConnectionHolder connection = connections[i];
long phyConnectTimeMillis = connection.getTimeMillis() - currentTimeMillis;//physical connection connected time
if( phyConnectTimeMillis > phyTimeoutMillis ){
evictList.add(connection);//if physical connection connected greater than phyTimeoutMillis, close the connection, for mysql 8 hours timeout
continue;
}
if (checkTime) {
long idleMillis = currentTimeMillis - connection.getLastActiveTimeMillis();
if (idleMillis >= minEvictableIdleTimeMillis) {
evictList.add(connection);
} else {
break;
}
} else {
evictList.add(connection);
}
}
int removeCount = evictList.size();
if (removeCount > 0) {
System.arraycopy(connections, removeCount, connections, 0, poolingCount - removeCount);
Arrays.fill(connections, poolingCount - removeCount, poolingCount, null);
poolingCount -= removeCount;
}
} finally {
lock.unlock();
}
for (DruidConnectionHolder item : evictList) {
Connection connection = item.getConnection();
JdbcUtils.close(connection);
destroyCount.incrementAndGet();
}
}
public int removeAbandoned() {
int removeCount = 0;
long currrentNanos = System.nanoTime();
List abandonedList = new ArrayList();
synchronized (activeConnections) {
Iterator iter = activeConnections.keySet().iterator();
for (; iter.hasNext();) {
DruidPooledConnection pooledConnection = iter.next();
if (pooledConnection.isRunning()) {
continue;
}
long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);
if (timeMillis >= removeAbandonedTimeoutMillis) {
iter.remove();
pooledConnection.setTraceEnable(false);
abandonedList.add(pooledConnection);
}
}
}
if (abandonedList.size() > 0) {
for (DruidPooledConnection pooledConnection : abandonedList) {
synchronized (pooledConnection) {
if (pooledConnection.isDisable()) {
continue;
}
}
JdbcUtils.close(pooledConnection);
pooledConnection.abandond();
removeAbandonedCount++;
removeCount++;
if (isLogAbandoned()) {
StringBuilder buf = new StringBuilder();
buf.append("abandon connection, owner thread: ");
buf.append(pooledConnection.getOwnerThread().getName());
buf.append(", connected at : ");
buf.append(pooledConnection.getConnectedTimeMillis());
buf.append(", open stackTrace\n");
StackTraceElement[] trace = pooledConnection.getConnectStackTrace();
for (int i = 0; i < trace.length; i++) {
buf.append("\tat ");
buf.append(trace[i].toString());
buf.append("\n");
}
buf.append("ownerThread current state is "+pooledConnection.getOwnerThread().getState() + ", current stackTrace\n");
trace = pooledConnection.getOwnerThread().getStackTrace();
for (int i = 0; i < trace.length; i++) {
buf.append("\tat ");
buf.append(trace[i].toString());
buf.append("\n");
}
LOG.error(buf.toString());
}
}
}
return removeCount;
}
以上源码可以看出每隔timeBetweenEvictionRunsMillis进行一次shrink(连接池大小收缩)检测,如果当前连接池中 的连接数量大于minIdle,则对超出minIdle的较早的连接进行空闲时间检测,如果某个连接在空闲了 minEvictableIdleTimeMillis时间后仍然没有使用,则被物理性的关闭掉。除了定时检测空闲连接以外,Druid还有一个 removeAbandoned机制,如果removeAbandoned为true,则在执行shrink后执行removeAbandoned(), 如果某个连接从被连接到当前时间超过removeAbandonedTimeoutMillis,则无论是否被使用都被强制物理性的关闭掉,同时将该连接 的abandoned设置为true。
public int getTransactionQueryTimeout() {
if (transactionQueryTimeout <= 0) {
return queryTimeout;
}
return transactionQueryTimeout;
}
public void setTransactionQueryTimeout(int transactionQueryTimeout) {
this.transactionQueryTimeout = transactionQueryTimeout;
}
/**
* Retrieves the number of seconds the driver will wait for a Statement object to execute. If the limit
* is exceeded, a SQLException is thrown.
*
* @return the current query timeout limit in seconds; zero means there is no limit
* @exception SQLException if a database access error occurs or this method is called on a closed
* Statement
* @see #setQueryTimeout
*/
public int getQueryTimeout() {
return queryTimeout;
}
/**
* Sets the number of seconds the driver will wait for a Statement object to execute to the given
* number of seconds. If the limit is exceeded, an SQLException is thrown. A JDBC driver must apply
* this limit to the execute, executeQuery and executeUpdate methods. JDBC
* driver implementations may also apply this limit to ResultSet methods (consult your driver vendor
* documentation for details).
*
* @param seconds the new query timeout limit in seconds; zero means there is no limit
* @exception SQLException if a database access error occurs, this method is called on a closed
* Statement or the condition seconds >= 0 is not satisfied
* @see #getQueryTimeout
*/
public void setQueryTimeout(int seconds) {
this.queryTimeout = seconds;
}
void initStatement(DruidPooledConnection conn, Statement stmt) throws SQLException {
boolean transaction = !conn.getConnectionHolder().isUnderlyingAutoCommit();
int queryTimeout = transaction ? getTransactionQueryTimeout() : getQueryTimeout();
if (queryTimeout > 0) {
stmt.setQueryTimeout(queryTimeout);
}
}
以上源码中可以看出在初始化Statement对象时,会设置其queryTimeout,如果conn开启了事务,则使用 transactionQueryTimeout,没开启就直接用queryTimeout。使用transactionQueryTimeout的时 候,如果该项没有设置,依然直接使用queryTimeout。此参数的作用是执行Statement对象时如果超过此时间,就抛出 SQLException。
有些数据库连接的时候有超时限制(mysql连接在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的情况,这时候设置一个 testWhileIdle参数为true,可以保证连接池内部定时检测连接的可用性,不可用的连接会被抛弃或者重建,最大情况的保证从连接池中得到的 Connection对象是可用的。当然,为了保证绝对的可用性,你也可以使用testOnBorrow为true(即在获取Connection对象时 检测其可用性),不过这样会影响性能。
http://www.itechzero.com/alibaba-druid-datasource-source-study-part-of-properties.html
相关推荐
**阿里巴巴数据库连接池Druid详解** Druid是阿里巴巴开源的一款高效、强大且可扩展的数据库连接池组件。作为Java世界中的优秀数据源管理工具,它不仅提供了基础的数据库连接池功能,还内置了丰富的监控和扩展特性,...
Druid是阿里巴巴开源的一款优秀的数据库连接池实现,它不仅提供了基本的连接池功能,还集成了监控、日志、SQL解析等高级特性。本篇文章将深入探讨Druid的核心特性和使用方法。 1. **简介** Druid是阿里巴巴在2010...
Druid是阿里巴巴开源的一款高效、强大的数据库连接池组件,它在性能、监控和扩展性方面表现出色。Druid不仅是一个连接池,还包含了SQL解析器、日志监控、SQL执行效率分析等功能,使得数据库的管理变得更加便捷和智能...
通过学习和研究这些源代码,开发者不仅可以了解Druid连接池的工作原理,还能学习到如何在项目中高效使用和自定义配置Druid,提升系统的稳定性和性能。同时,对于Java的数据库编程和设计模式也会有更深入的理解。
在Druid源码中,我们可以重点关注以下几个核心模块: 1. 数据源(DataSource):作为数据库连接池的核心,DruidDataSource提供了初始化、连接获取与释放、连接池状态监控等功能。它支持配置各种参数,如最大连接数...
Druid是阿里巴巴开源的一款高性能、功能强大的数据库连接池组件,它在Java编程领域中被广泛使用,特别是在大型互联网项目中。Druid提供了监控、SQL解析、防御性编程等功能,旨在提高数据库连接管理的效率和系统的...
Druid是一个广泛使用的Java数据库连接池(JDBC Connection Pool)框架,由阿里巴巴开源。它不仅提供了数据库连接池功能,还包含SQL解析、监控统计、日志等特性,旨在提高数据库操作的性能和稳定性。在本压缩包"druid...
Druid是阿里巴巴开源的一个高性能、多功能的数据库连接池组件,它在Java开发中广泛应用于各种项目的数据库管理。在1.2.8版本中,Druid提供了一系列优化和改进,旨在提高数据库访问性能,增强监控能力,并简化数据库...
Druid,一个专为监控而设计的数据库连接池,由阿里巴巴开源并广泛应用于各种项目中,尤其是阿里云的分布式关系型数据库服务DRDS。这个压缩包`druid-master`包含了Druid项目的源代码,可供开发者深入学习和定制。 ...
在Java开发中,Druid是一个广泛使用的数据库连接池实现,由阿里巴巴开源并维护。标题提到的"数据库连接池,druid依赖包"正是针对这个组件。 Druid是一个多功能的数据源库,不仅提供了数据库连接池服务,还集成了SQL...
通过阅读和分析Druid源码,我们可以学习到如何设计和实现一个高性能的数据库连接池,同时也能了解阿里巴巴在开发过程中的一些最佳实践和设计模式。这对于提升个人技能,特别是Java后端开发能力,有着极大的帮助。...
Druid是阿里巴巴开源的一个数据库连接池组件,它在Java开发中被广泛应用,特别是在大数据量、高并发的系统中,因其高效、稳定和强大的监控能力而受到赞誉。本源码包为`druid-1.1.6`版本,包含了Druid的核心功能及其...
总之,阿里数据库连接池Druid是Java开发中的重要工具,它提供的不仅仅是简单的数据库连接管理,还包括了丰富的监控、安全防护和性能优化功能。结合源码学习和配置文件的使用,开发者可以更好地掌握Druid的精髓,提升...
该项目是基于Java开发的Druid数据库连接池设计源码,包含4690个文件,涵盖4070个Java源文件、297个SQL文件、102个TXT文件、94个DAT文件、19个PROPERTIES文件、19个HTML文件、15个XML文件、13个JAR文件、8个PKS文件和...
- Druid:阿里巴巴开源的数据库连接池,除了基本的连接池功能外,还提供了监控、日志、拦截器等功能,便于对数据库访问进行诊断和优化。 4. 使用步骤: - 引入依赖:在项目中引入对应连接池的jar包或Maven/Gradle...
Druid连接池,全称是Druid Data Source,是由阿里巴巴开源的一个高效、强大的数据库连接池组件。在Java开发中,数据库连接池是管理数据库连接的重要工具,它可以提高数据库访问的效率,减少系统资源的浪费,是现代...
通过引入这个启动器,我们可以快速地在Spring Boot项目中配置并使用Druid数据库连接池。`druid-spring-boot-starter`自动配置了数据源、监控统计以及相关的初始化设置,使得开发者无需编写大量繁琐的XML配置或Java...
这个资料包中的`druid-1.0.9`和`C3P0`文件可能包含了这两个连接池的库文件,以及相关的配置示例,对于学习者来说,可以通过阅读源码和实践配置,深入了解它们的内部实现和使用方法。在实际项目中,结合提供的配置...
该项目为阿里巴巴数据库事业部推出的针对监控...该连接池由Druid驱动,支持阿里云DRDS和阿里巴巴TDDL,旨在提供高效、稳定的数据库连接管理。2018年,该项目荣获开源中国最受欢迎开源软件评选奖项,欢迎参与投票支持。
而Druid是阿里巴巴开源的一个高性能、多功能的Java数据库连接池,它不仅提供了基本的连接池功能,还具备SQL拦截、统计分析、监控等功能,对于优化数据库访问性能有着显著帮助。 要配置Druid数据源,我们首先需要在...