Druid 是一个 JDBC 组件库,包含数据库连接池、SQL Parser 等组件, 被大量业务和技术产品使用或集成,经历过最严苛线上业务场景考验,是你值得信赖的技术产品。

Druid有以下几点优势或好处:

替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。

可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。

数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。

SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。

扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。


1、Maven

<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.1.22</version>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>8.0.20</version>
</dependency>

2、纯代码使用:

import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;

class TestDruid {
   // 数据库驱动名称
   final static String driver = "com.mysql.cj.jdbc.Driver";
   // 数据库连接地址
   final static String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT";
   // 数据库用户名
   final static String user = "test";
   // 数据库密码
   final static String passwd = "1";
   // 连接池初始化大小
   final static int initialSize = 5;
   // 连接池最小空闲
   final static int minPoolSize = 10;
   // 连接池最大连接数量
   final static int maxPoolSize = 50;
   // 最小逐出时间,100秒
   final static int maxIdleTime = 100000;
   // 连接失败重试次数
   final static int retryAttempts = 10;
   // 当连接池连接耗尽时获取连接数
   final static int acquireIncrement = 5;
   // 查询次数
   final static int count = 10;
   // Druid数据源
   static DruidDataSource druidDataSource = null;

   @BeforeAll
   static void init() {
      druidDataSource = new DruidDataSource();
      druidDataSource.setUsername(user);
      druidDataSource.setUrl(jdbcUrl);
      druidDataSource.setPassword(passwd);
      druidDataSource.setDriverClassName(driver);
      druidDataSource.setInitialSize(initialSize);
      druidDataSource.setMaxActive(maxPoolSize);
      druidDataSource.setMaxWait(maxIdleTime);
      druidDataSource.setTestWhileIdle(false);
      druidDataSource.setTestOnReturn(false);
      druidDataSource.setTestOnBorrow(false);
   }

   @Test
   void test_Insert() throws SQLException {
      DruidPooledConnection connection = druidDataSource.getConnection();
      PreparedStatement stmt = connection.prepareStatement("INSERT INTO sys_user(username,password) VALUES(?,?)");

      stmt.setString(1, "admin");
      stmt.setString(2, "123456");
      stmt.execute();
   }
}


3、Spring/SpringMVC中使用

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
   <property name="driverClassName"><value>com.mysql.cj.jdbc.Driver</value></property>
   <property name="url"><value>jdbc:mysql://127.0.0.1:3306/test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF8&amp;serverTimezone=GMT</value></property>
   <property name="username"><value>test</value></property>
   <property name="password"><value>1</value></property>

   <!-- 通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall -->
   <property name="filters" value="stat,log4j" />
   <!-- 最大并发连接数 -->
   <property name="maxActive" value="30" />
   <!-- 初始化连接数量 -->
   <property name="initialSize" value="5" />
   <!-- 配置获取连接等待超时的时间 -->
   <property name="maxWait" value="60000" />
   <!-- 最小空闲连接数 -->
   <property name="minIdle" value="5" />
   <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
   <property name="timeBetweenEvictionRunsMillis" value="60000" />
   <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
   <property name="minEvictableIdleTimeMillis" value="300000" />
   <property name="validationQuery" value="SELECT 'x'" />
   <property name="testWhileIdle" value="true" />
   <property name="testOnBorrow" value="false" />
   <property name="testOnReturn" value="false" />
   <property name="poolPreparedStatements" value="false" />
   <property name="maxOpenPreparedStatements" value="100" />
   <!-- 打开removeAbandoned功能(连接泄漏监测,怀疑存在泄漏之后再打开) -->
   <property name="removeAbandoned" value="true" />
   <!-- 1800秒,也就是30分钟 -->
   <property name="removeAbandonedTimeout" value="1800" />
   <!-- 关闭abanded连接时输出错误日志 -->
   <property name="logAbandoned" value="true" />
</bean>


4、DruidDataSource配置兼容DBCP,但个别配置的语意有所区别,如下表格:

配置

缺省值

说明

name

 

配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。详情-点此处。

url

 

连接数据库的url,不同数据库不一样。例如:
mysql : jdbc:mysql://10.20.153.104:3306/druid2
oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto

username

 

连接数据库的用户名

password

 

连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里

driverClassName

根据url自动识别

这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName

initialSize

0

初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时

maxActive

8

最大连接池数量

maxIdle

8

已经不再使用,配置了也没效果

minIdle

 

最小连接池数量

maxWait

 

获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。

poolPreparedStatements

FALSE

是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。

maxPoolPreparedStatementPerConnectionSize

-1

要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100

validationQuery

 

用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。

validationQueryTimeout

 

单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法

testOnBorrow

TRUE

申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。

testOnReturn

FALSE

归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。

testWhileIdle

FALSE

建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。

keepAlive

FALSE(1.0.28)

连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。

timeBetweenEvictionRunsMillis

1分钟(1.0.14)

有两个含义:

1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。

2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明

numTestsPerEvictionRun

30分钟(1.0.14)

不再使用,一个DruidDataSource只支持一个EvictionRun

minEvictableIdleTimeMillis

 

连接保持空闲而不被驱逐的最小时间

connectionInitSqls

 

物理连接初始化的时候执行的sql

exceptionSorter

根据dbType自动识别

当数据库抛出一些不可恢复的异常时,抛弃连接

filters

 

属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
监控统计用的filter:stat
日志用的filter:log4j
防御sql注入的filter:wall

proxyFilters

 

类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系


5、Druid监控

同Proxool一样Druid提供了监控功能,而且更强大;

配置到web.xml中,即要通过浏览器输入/druid/访问,如果设置了用户用户密码,则开始会进入登录页面,如下图。

<!-- 连接池 启用Web监控统计功能 start -->
<filter>
  <filter-name>DruidWebStatFilter</filter-name>
  <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
  <init-param>
   <param-name>exclusions</param-name>
   <param-value>*.js,*mp3,*.swf,*.xls,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>DruidWebStatFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
  <servlet-name>DruidStatView</servlet-name>
  <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
  <init-param>
   <param-name>resetEnable</param-name><!-- 允许清空统计数据 -->
   <param-value>true</param-value>
  </init-param>
  <init-param>
   <param-name>loginUsername</param-name><!-- 用户名 -->
   <param-value>admin</param-value>
  </init-param>
  <init-param>
   <param-name>loginPassword</param-name><!-- 密码 -->
   <param-value>123456</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>DruidStatView</servlet-name>
  <url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<!-- 连接池 启用Web监控统计功能 end -->


下图是Druid官网给出的几种连接池的对比表格,从数据上来看Druid在各方面都占有很大的优势,实际情况还需要实践去检验。

LRU

LRU是一个性能关键指标,特别Oracle,每个Connection对应数据库端的一个进程,如果数据库连接池遵从LRU,有助于数据库服务器优化,这是重要的指标。在测试中,Druid、DBCP、Proxool、JBoss是遵守LRU的。BoneCP、C3P0则不是。BoneCP在mock环境下性能可能好,但在真实环境中则就不好了。

PSCache

PSCache是数据库连接池的关键指标。在Oracle中,类似SELECT NAME FROM USER WHERE ID = ?这样的SQL,启用PSCache和不启用PSCache的性能可能是相差一个数量级的。Proxool是不支持PSCache的数据库连接池,如果你使用Oracle、SQL Server、DB2、Sybase这样支持游标的数据库,那你就完全不用考虑Proxool。

PSCache-Oracle-Optimized

Oracle 10系列的Driver,如果开启PSCache,会占用大量的内存,必须做特别的处理,启用内部的EnterImplicitCache等方法优化才能够减少内存的占用。这个功能只有DruidDataSource有。如果你使用的是Oracle Jdbc,你应该毫不犹豫采用DruidDataSource。

ExceptionSorter

ExceptionSorter是一个很重要的容错特性,如果一个连接产生了一个不可恢复的错误,必须立刻从连接池中去掉,否则会连续产生大量错误。这个特性,目前只有JBossDataSource和Druid实现。Druid的实现参考自JBossDataSource。

监控

DruidDataSource自身提供有NotEmptyWaitCount、PSCahcheHitCount等有用的监控属性,通过配置StatFilter能够监控SQL的执行情况。

扩展

DruidDataSource提供基于Filter-Chain模式的模式的扩展。