Druid连接池学习笔记

Druid连接池

声明:以下内容均来自GitHub及其他博客的整理总结,仅供个人学习使用。

1.   Druid连接池简介

1.1 Druid简介

DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池。

1.2 Druid组成

Druid是一个JDBC组件,它包括三个部分:

l  基于Filter-Chain模式的插件体系

l  DruidDataSource 高效可管理的数据库连接池

l  SQLParser

1.3 Druid功能简介

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

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

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

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

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

2.   基本参数配置

2.1 配置参数

配置

缺省值

说明

name

 

配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 
如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this)

jdbcUrl

 

连接数据库的url,不同数据库不一样。例如: 
mysql : jdbc:mysql://10.20.153.104:3306/druid2 
oracle : 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

 

最小连接池数量

maxWait

 

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

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检测连接是否有效。

timeBetweenEvictionRunsMillis

 

有两个含义:
1) Destroy线程会检测连接的间隔时间

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

numTestsPerEvictionRun

 

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

minEvictableIdleTimeMillis

 

 

connectionInitSqls

 

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

exceptionSorter

根据dbType自动识别

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

filters

 

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

proxyFilters

 

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

2.2 参考配置

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

      <!-- 基本属性 url、user、password -->

      <property name="url" value="${jdbc_url}" />

      <property name="username" value="${jdbc_user}" />

      <property name="password" value="${jdbc_password}" />

       

      <!-- 配置初始化大小、最小、最大 -->

      <property name="initialSize" value="1" />

      <property name="minIdle" value="1" />

      <property name="maxActive" value="20" />

  

      <!-- 配置获取连接等待超时的时间 -->

      <property name="maxWait" value="60000" />

  

      <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->

      <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" />

  

      <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->

      <property name="poolPreparedStatements" value="true" />

      <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />

  

      <!-- 配置监控统计拦截的filters -->

      <property name="filters" value="stat" />

  </bean>

通常来说,只需要修改initialSize、minIdle、maxActive。

如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。分库分表较多的数据库,建议配置为false。

3.   与其他连接池的对比

3.1 性能比较

功能

DBCP

Druid

c3p0

tomcat-jdbc

HikariCP

是否支持PSCache

监控

jmx

jmx/log/http

jmx,log

jmx

jmx

扩展性

sql拦截及解析

支持

代码

简单

中等

复杂

简单

简单

特点

依赖于common-pool

阿里开源,功能全面

历史久远,代码逻辑复杂,且不易维护

 

优化力度大,功能简单,起源于boneCP

连接池管理

LinkedBlockingDeque

数组

 

FairBlockingQueue

threadlocal+CopyOnWriteArrayList

(1)性能方面 HikariCP>Druid>tomcat-jdbc>dbcp>c3p0。HikariCP的高性能得益于最大限度的避免锁竞争。

(2)Druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。

(3)综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池。

(4)可开启prepareStatement缓存,对性能会有大概20%的提升。

3.2 特有功能说明

3.2.1 ExceptionSorter

当网络断开或者数据库服务器Crash时,连接池里面会存在“不可用连接”,连接池需要一种机制剔除这些“不可用连接”。在Druid和JBoss连接池中,剔除“不可用连接”的机制称为ExceptionSorter,实现的原理是根据异常类型/Code/Reason/Message来识别“不可用连接”。没有类似ExceptionSorter的连接池,在数据库重启或者网络中断之后,不能恢复工作,所以ExceptionSorter是连接池是否稳定的重要标志。在Druid中,会根据连接池连接数据库的类型自动匹配不同类型的ExceptionSorter,不需要额外配置。

以下是Druid内置的ExceptionSorter:

dbType

ExceptionSorter Implementation

description

DB2

com.alibaba.druid.pool.vendor.DB2ExceptionSorter

 

Informix

com.alibaba.druid.pool.vendor.InformixExceptionSorter

 

SQL Server

com.alibaba.druid.pool.vendor.MSSQLValidConnectionChecker

 

MySQL

com.alibaba.druid.pool.vendor.MySqlExceptionSorter

支持OceanBase和阿里云RDS特定的ErrorCode

Oracle

com.alibaba.druid.pool.vendor.OracleExceptionSorter

 

Postgresql

com.alibaba.druid.pool.vendor.PGExceptionSorter

 

Sybase

com.alibaba.druid.pool.vendor.SybaseExceptionSorter

 
3.2.2 Oracle数据库下PreparedStatementCache内存问题解决方案

Oracle支持游标,一个PreparedStatement对应服务器一个游标,如果PreparedStatement被缓存起来重复执行,PreparedStatement没有被关闭,服务器端的游标就不会被关闭,性能提高非常显著。在类似SELECT * FROM T WHERE ID =?这样的场景,性能可能是一个数量级的提升。

由于PreparedStatementCache性能提升明显,DruidDataSource、DBCP、JBossDataSource、WeblogicDataSource都实现了PreparedStatementCache。

但是Oracle 10系列的Driver,如果开启PSCache,会占用大量的内存。通过分析发现占内存的是字段char[] defineChars。defineChars大小的计算公式是这样的:

defineChars大小 = rowSize * rowPrefetchCount

rowPrefetchCount在Oracle中,缺省值为10。

其中rowSize是执行查询设计的每一列的大小的和。计算公式是:

rowSize = col_1_size + col_2_size + ... + col_n_size

有些列数据类型是varchar2(4000),于是rowSize巨大,很多个表关联的SQL,rowSize可能高达数十K,再乘以rowPrefetchCount,defineChars大小接近1M。可以想想,maxPoolSize设置为30,PreparedStatementCacheSize设置为50的场景下,是可能导致PreparedStatementCache占据上G的内存。

实际占据内存的公式:

占据内存大小峰值 = defineChars大小 * PreparedStatementCacheSize * MaxPoolSize

一个应用运行的SQL大约数百条,PreparedStatementCacheSize为50,PreparedStatementCache的算法为LRU,很多的SQL执行之后,在Cache中HitCount为0就被淘汰了,淘汰的过程,其位置从第1移到第50,这个漫长的过程导致了defineChars不能够被young gc回收。

解决方案:

使用OracleDriver提供的PreparedStatementCache支持方法,清理PreparedStatement所持有的buffer。 Oracle在10.x和11.x的Driver中,都提供了如下管理PreparedStatementCache的接口,如下:

package oracle.jdbc.internal;

 import java.sql.SQLException;

 public interface OraclePreparedStatement extends oracle.jdbc.OraclePreparedStatement, OracleStatement {

     public void enterImplicitCache() throws SQLException;

     public void exitImplicitCacheToActive() throws SQLException;

     public void exitImplicitCacheToClose() throws SQLException;

 }

DruidDataSource在管理Oracle PreparedStatementCache时,调用了上述方法。当调用了enterImplicitCache之后,T4CPreparedStatement中的defineChars和defineBytes都会被清空。

测试表明,通过上述处理,能够有效降低内存。

4.   Filter配置说明

4.1 配置Filter

DruidDataSource支持通过Filter-Chain模式进行扩展,类似Serlvet的Filter,扩展十分方便,你可以拦截任何JDBC的方法。

有两种配置Filter的方式,一种是配置filters属性,一种是配置proxyFilters属性。filters和proxyFilters的配置是组合关系,而不是替换关系。

4.1.1 配置Filter属性

配置filters属性比较简单,filters的类型是字符串,多个filter使用逗号隔开。例如:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

      init-method="init" destroy-method="close">

      <property name="url" value="jdbc:derby:memory:spring-test;create=true" />

      <property name="initialSize" value="1" />

      <property name="maxActive" value="20" />

      <property name="filters" value="stat,log4j" />

  </bean>

Filters属性的配置使用别名或者全类名,以下是内置Filter的别名:

Filter类名

别名

default

com.alibaba.druid.filter.stat.StatFilter

stat

com.alibaba.druid.filter.stat.StatFilter

mergeStat

com.alibaba.druid.filter.stat.MergeStatFilter

encoding

com.alibaba.druid.filter.encoding.EncodingConvertFilter

log4j

com.alibaba.druid.filter.logging.Log4jFilter

log4j2

com.alibaba.druid.filter.logging.Log4j2Filter

slf4j

com.alibaba.druid.filter.logging.Slf4jLogFilter

commonlogging

com.alibaba.druid.filter.logging.CommonsLogFilter

wall

com.alibaba.druid.wall.WallFilter

4.1.2 配置proxyFilter属性

proxyFilters的类型是List,使用proxyFilters配置,可以有更多的配置选项:

  <bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">

  </bean>

 

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

      init-method="init" destroy-method="close">

      <property name="url" value="jdbc:derby:memory:spring-test;create=true" />

      <property name="initialSize" value="1" />

      <property name="maxActive" value="20" />

      <property name="proxyFilters">

          <list>

              <ref bean="stat-filter" />

          </list>

      </property>

  </bean>

4.2 配置StatFilter

Druid内置提供一个StatFilter,用于统计监控信息。

4.2.1 别名配置

StatFilter的别名是stat,这个别名映射配置信息保存在druid-xxx.jar!/META-INF/druid-filter.properties。在spring中使用别名配置方式如下:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

       ... ...

       <property name="filters" value="stat" />

  </bean>

4.2.2 组合配置

StatFilter可以和其他的Filter配置使用,比如:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

       ... ...

       <property name="filters" value="stat,log4j" />

  </bean>

在上面的配置中,StatFilter和Log4jFilter组合使用。

4.2.3 通过ProxyFilters属性配置

别名配置是通过filters属性配置的,filters属性的类型是String。如果需要通过bean的方式配置,使用proxyFilters属性。

<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">

       <property name="slowSqlMillis" value="10000" />

       <property name="logSlowSql" value="true" />

</bean>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

       init-method="init" destroy-method="close">

       ... ...

       <property name="filters" value="log4j" />

       <property name="proxyFilters">

              <list>

                     <ref bean="stat-filter" />

              </list>

       </property>

</bean>

其中filters和proxyFilters属性是组合关系的,不是替换的,在上面的配置中,dataSource有了两个Filter,StatFilter和Log4jFilter。

4.2.4 SQL合并配置

当你程序中存在没有参数化的sql执行时,sql统计的效果会不好。比如:

select * from t where id = 1

select * from t where id = 2

select * from t where id = 3

在统计中,显示为3条sql,这不是我们希望要的效果。StatFilter提供合并的功能,能够将这3个SQL合并为如下的SQL:

select * from t where id = ?

配置StatFilter的mergeSql属性:

<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">

       <property name="mergeSql" value="true" />

</bean>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

       init-method="init" destroy-method="close">

       ... ...

       <property name="proxyFilters">

              <list>

                     <ref bean="stat-filter" />

              </list>

       </property>

</bean>

StatFilter支持一种简化配置方式,和上面的配置等同的。如下:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

     ... ...

     <property name="filters" value="mergeStat" />

</bean>

mergeStat是的MergeStatFilter缩写,我们看MergeStatFilter的实现:

public class MergeStatFilter extends StatFilter {

     public MergeStatFilter() {

            super.setMergeSql(true);

     }

}

从实现代码来看,仅仅是一个mergeSql的缺省值。

也可以通过connectProperties属性来打开mergeSql功能,例如:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

     ... ...

     <property name="filters" value="stat" />

     <property name="connectionProperties" value="druid.stat.mergeSql=true" />

  </bean>

或者通过增加JVM的参数配置:

-Ddruid.stat.mergeSql=true

在druid-0.2.17版本之后,sql合并支持tddl,能够对分表进行合并。

4.2.5 慢SQL记录

StatFilter属性slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢。slowSqlMillis的缺省值为3000,也就是3秒。

<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">

       <property name="slowSqlMillis" value="10000" />

       <property name="logSlowSql" value="true" />

</bean>

在上面的配置中,slowSqlMillis被修改为10秒,并且通过日志输出执行慢的SQL。

slowSqlMillis属性也可以通过connectProperties来配置,例如:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

     ... ...

     <property name="filters" value="stat" />

     <property name="connectionProperties" value="druid.stat.slowSqlMillis=5000" />

  </bean>

4.2.6 合并多个DruidDataSource的监控数据

缺省多个DruidDataSource的监控数据是各自独立的,在Druid-0.2.17版本之后,支持配置公用监控数据,配置参数为useGlobalDataSourceStat。例如:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

     ... ...

     <property name="useGlobalDataSourceStat" value="true" />

  </bean>

或者通过jvm启动参数来指定,例如:

-Ddruid.useGlobalDataSourceStat=true

全部使用jvm启动参数来配置,可以这样:

-Ddruid.filters=mergeStat -Ddruid.useGlobalDataSourceStat=true

4.3 配置LogFilter

Druid内置提供了四种LogFilter(Log4jFilter、Log4j2Filter、CommonsLogFilter、Slf4jLogFilter),用于输出JDBC执行的日志。

4.3.1 别名映射

在druid-xxx.jar!/META-INF/druid-filter.properties文件中描述了这四种Filter的别名:

  druid.filters.log4j=com.alibaba.druid.filter.logging.Log4jFilter

  druid.filters.log4j2=com.alibaba.druid.filter.logging.Log4j2Filter

  druid.filters.slf4j=com.alibaba.druid.filter.logging.Slf4jLogFilter

  druid.filters.commonlogging=com.alibaba.druid.filter.logging.CommonsLogFilter

  druid.filters.commonLogging=com.alibaba.druid.filter.logging.CommonsLogFilter

他们的别名分别是log4j、log4j2、slf4j、commonlogging和commonLogging。其中commonlogging和commonLogging只是大小写不同。

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

      init-method="init" destroy-method="close">

      ... ...

      <property name="filters" value="stat,log4j" />

  </bean>

4.3.2 loggerName配置

LogFilter都是缺省使用四种不同的Logger执行输出,看实现代码:

  public abstract class LogFilter {

      protected String          dataSourceLoggerName                 = "druid.sql.DataSource";

      protected String          connectionLoggerName                 = "druid.sql.Connection";

      protected String          statementLoggerName                  = "druid.sql.Statement";

      protected String          resultSetLoggerName                  = "druid.sql.ResultSet";

  }

你可以根据你的需要修改,在log4j.properties文件上做配置时,注意配置使用相关的logger。

4.3.3 配置输出日志

缺省输入的日志信息全面,但是内容比较多,有时候我们需要定制化配置日志输出。

<bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter">

    <property name="resultSetLogEnabled" value="false" />

</bean>

 

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

    ...

    <property name="proxyFilters">

        <list>

            <ref bean="log-filter"/>

        </list>

    </property>

</bean>

 

参数

说明

dataSourceLogEnabled

所有DataSource相关的日志

connectionLogEnabled

所有连接相关的日志

connectionLogErrorEnabled

所有连接上发生异常的日志

statementLogEnabled

所有Statement相关的日志

statementLogErrorEnabled

所有Statement发生异常的日志

resultSetLogEnabled

 

resultSetLogErrorEnabled

 

connectionConnectBeforeLogEnabled

 

connectionConnectAfterLogEnabled

 

connectionCommitAfterLogEnabled

 

connectionRollbackAfterLogEnabled

 

connectionCloseAfterLogEnabled

 

statementCreateAfterLogEnabled

 

statementPrepareAfterLogEnabled

 

statementPrepareCallAfterLogEnabled

 

statementExecuteAfterLogEnabled

 

statementExecuteQueryAfterLogEnabled

 

statementExecuteUpdateAfterLogEnabled

 

statementExecuteBatchAfterLogEnabled

 

statementCloseAfterLogEnabled

 

statementParameterSetLogEnabled

 

resultSetNextAfterLogEnabled

 

resultSetOpenAfterLogEnabled

 

resultSetCloseAfterLogEnabled

 
4.3.4 log4j.properties配置

如果你使用log4j,可以通过log4j.properties文件配置日志输出选项,例如:

  log4j.logger.druid.sql=warn,stdout

  log4j.logger.druid.sql.DataSource=warn,stdout

  log4j.logger.druid.sql.Connection=warn,stdout

  log4j.logger.druid.sql.Statement=warn,stdout

  log4j.logger.druid.sql.ResultSet=warn,stdout

4.3.5 输出可执行的SQL

Java启动参数配置方式:

  -Ddruid.log.stmt.executableSql=true

logFilter参数直接配置

  <bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter">

        <property name="statementExecutableSqlLogEnable" value="true" />

  </bean>

4.4 配置WallFilter

4.4.1 使用缺省配置的WallFilter

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

      ...

      <property name="filters" value="wall"/>

  </bean>

4.4.2 结合其他Filter一起使用

WallFilter可以结合其他Filter一起使用,例如:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

      ...

      <property name="filters" value="wall,stat"/>

  </bean>

这样,拦截检测的时间不在StatFilter统计的SQL执行时间内。

如果希望StatFilter统计的SQL执行时间内,则使用如下配置:

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

      ...

      <property name="filters" value="stat,wall"/>

  </bean>

4.4.3 指定dbType

有时候,一些应用框架做了自己的JDBC Proxy Driver,是的DruidDataSource无法正确识别数据库的类型,则需要特别指定,如下:

  <bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">

      <property name="dbType" value="mysql" />

  </bean>

 

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

      ...

      <property name="proxyFilters">

          <list>

              <ref bean="wall-filter"/>

          </list>

      </property>

  </bean>

4.4.4 指定配置装载的目录

缺省情况下,配置装载的目录如下:

数据库类型

目录

mysql

META-INF/druid/wall/mysql

oracle

META-INF/druid/wall/oracle

sqlserver

META-INF/druid/wall/sqlserver

postgres

META-INF/druid/wall/postgres

从配置目录中以下文件中读取配置:

  deny-variant.txt

  deny-schema.txt

  deny-function.txt

  deny-table.txt

  deny-object.txt

指定配置装载的目录是可以指定,例如:

  <bean id="wall-filter-config" class="com.alibaba.druid.wall.WallConfig" init-method="init">

      <!-- 指定配置装载的目录  -->

      <property name="dir" value="META-INF/druid/wall/mysql" />

  </bean>

  

  <bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">

      <property name="dbType" value="mysql" />

      <property name="config" ref="wall-filter-config" />

  </bean>

  

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

      ...

      <property name="proxyFilters">

          <list>

              <ref bean="wall-filter"/>

          </list>

      </property>

  </bean>

4.4.5 WallConfig详细说明

Druid提供了WallFilter,它是基于SQL语义分析来实现防御SQL注入攻击的。

4.4.5.1 本身的配置

配置项

缺省值

dir

按照dbType分别配置:

mysql : META-INF/druid/wall/mysql

oracle : META-INF/druid/wall/oracle

sqlserver : META-INF/druid/wall/sqlserver

4.4.5.2 拦截配置-语句

配置项

缺省值

描述

selelctAllow

true

是否允许执行SELECT语句

selectAllColumnAllow

true

是否允许执行SELECT * FROM T这样的语句。如果设置为false,不允许执行select * from t,但select * from (select id, name from t) a。这个选项是防御程序通过调用select *获得数据表的结构信息。

selectIntoAllow

true

SELECT查询中是否允许INTO字句

deleteAllow

true

是否允许执行DELETE语句

updateAllow

true

是否允许执行UPDATE语句

insertAllow

true

是否允许执行INSERT语句

replaceAllow

true

是否允许执行REPLACE语句

mergeAllow

true

是否允许执行MERGE语句,这个只在Oracle中有用

callAllow

true

是否允许通过jdbc的call语法调用存储过程

setAllow

true

是否允许使用SET语法

truncateAllow

true

truncate语句是危险,缺省打开,若需要自行关闭

createTableAllow

true

是否允许创建表

alterTableAllow

true

是否允许执行Alter Table语句

dropTableAllow

true

是否允许修改表

commentAllow

false

是否允许语句中存在注释,Oracle的用户不用担心,Wall能够识别hints和注释的区别

noneBaseStatementAllow

false

是否允许非以上基本语句的其他语句,缺省关闭,通过这个选项就能够屏蔽DDL。

multiStatementAllow

false

是否允许一次执行多条语句,缺省关闭

useAllow

true

是否允许执行mysql的use语句,缺省打开

describeAllow

true

是否允许执行mysql的describe语句,缺省打开

showAllow

true

是否允许执行mysql的show语句,缺省打开

commitAllow

true

是否允许执行commit操作

rollbackAllow

true

是否允许执行roll back操作

如果把selectIntoAllow、deleteAllow、updateAllow、insertAllow、mergeAllow都设置为false,这就是一个只读数据源了。

4.4.5.3 拦截配置-永真条件

配置项

缺省值

描述

selectWhereAlwayTrueCheck

true

检查SELECT语句的WHERE子句是否是一个永真条件

selectHavingAlwayTrueCheck

true

检查SELECT语句的HAVING子句是否是一个永真条件

deleteWhereAlwayTrueCheck

true

检查DELETE语句的WHERE子句是否是一个永真条件

deleteWhereNoneCheck

false

检查DELETE语句是否无where条件,这是有风险的,但不是SQL注入类型的风险

updateWhereAlayTrueCheck

true

检查UPDATE语句的WHERE子句是否是一个永真条件

updateWhereNoneCheck

false

检查UPDATE语句是否无where条件,这是有风险的,但不是SQL注入类型的风险

conditionAndAlwayTrueAllow

false

检查查询条件(WHERE/HAVING子句)中是否包含AND永真条件

conditionAndAlwayFalseAllow

false

检查查询条件(WHERE/HAVING子句)中是否包含AND永假条件

conditionLikeTrueAllow

true

检查查询条件(WHERE/HAVING子句)中是否包含LIKE永真条件

4.4.5.4        其他拦截配置

配置项

缺省值

描述

selectIntoOutfileAllow

false

SELECT ... INTO OUTFILE 是否允许,这个是mysql注入攻击的常见手段,缺省是禁止的

selectUnionCheck

true

检测SELECT UNION

selectMinusCheck

true

检测SELECT MINUS

selectExceptCheck

true

检测SELECT EXCEPT

selectIntersectCheck

true

检测SELECT INTERSECT

mustParameterized

false

是否必须参数化,如果为True,则不允许类似WHERE ID = 1这种不参数化的SQL

strictSyntaxCheck

true

是否进行严格的语法检测,Druid SQL Parser在某些场景不能覆盖所有的SQL语法,出现解析SQL出错,可以临时把这个选项设置为false,同时把SQL反馈给Druid的开发者。

conditionOpXorAllow

false

查询条件中是否允许有XOR条件。XOR不常用,很难判断永真或者永假,缺省不允许。

conditionOpBitwseAllow

true

查询条件中是否允许有"&"、"~"、"|"、"^"运算符。

conditionDoubleConstAllow

false

查询条件中是否允许连续两个常量运算表达式

minusAllow

true

是否允许SELECT * FROM A MINUS SELECT * FROM B这样的语句

intersectAllow

true

是否允许SELECT * FROM A INTERSECT SELECT * FROM B这样的语句

constArithmeticAllow

true

拦截常量运算的条件,比如说WHERE FID = 3 - 1,其中"3 - 1"是常量运算表达式。

limitZeroAllow

false

是否允许limit 0这样的语句

selectLimit

-1

配置最大返回行数,如果select语句没有指定最大返回行数,会自动修改selct添加返回限制

4.4.5.5  禁用对象检测配置

配置项

缺省值

描述

tableCheck

true

检测是否使用了禁用的表

schemaCheck

true

检测是否使用了禁用的Schema

functionCheck

true

检测是否使用了禁用的函数

objectCheck

true

检测是否使用了“禁用对对象”

variantCheck

true

检测是否使用了“禁用的变量”

readOnlyTables

指定的表只读,不能够在SELECT INTO、DELETE、UPDATE、INSERT、MERGE中作为"被修改表"出现<

4.4.5.6 JDBC相关配置

配置项

缺省值

描述

metadataAllow

true

是否允许调用Connection.getMetadata方法,这个方法调用会暴露数据库的表信息

wrapAllow

true

是否允许调用Connection/Statement/ResultSet的isWrapFor和unwrap方法,这两个方法调用,使得有办法拿到原生驱动的对象,绕过WallFilter的检测直接执行SQL。

4.4.5.7 WallFilter配置说明

配置项

缺省值

描述

logViolation

false

对被认为是攻击的SQL进行LOG.error输出

throwException

true

对被认为是攻击的SQL抛出SQLExcepton

config

  

provider

  

刚开始引入WallFilter的时候,把logViolation设置为true,而throwException设置为false。就可以观察是否存在违规的情况,同时不影响业务运行。

4.5 配置WebStatFilter

WebStatFilter用于采集web-jdbc关联监控的数据。

4.5.1 web.xml配置

  <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,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>

     </init-param>

  </filter>

  <filter-mapping>

     <filter-name>DruidWebStatFilter</filter-name>

     <url-pattern>/*</url-pattern>

  </filter-mapping>

4.5.2 exclusions配置

经常需要排除一些不必要的url,比如*.js,/jslib/*等等。配置在init-param中。比如:

       <init-param>

            <param-name>exclusions</param-name>

            <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>

     </init-param>

4.5.3 sessionStatMaxCount配置

缺省sessionStatMaxCount是1000个。你可以按需要进行配置,比如:

       <init-param>

            <param-name>sessionStatMaxCount</param-name>

            <param-value>1000</param-value>

     </init-param>

4.5.4 sessionStatEnable配置

你可以关闭session统计功能,比如:

       <init-param>

            <param-name>sessionStatEnable</param-name>

            <param-value>false</param-value>

     </init-param>

4.5.5 principalSessionName配置

你可以配置principalSessionName,使得druid能够知道当前的session的用户是谁。比如:

       <init-param>

            <param-name>principalSessionName</param-name>

            <param-value>xxx.user</param-value>

     </init-param>

根据需要,把其中的xxx.user修改为你user信息保存在session中的sessionName。

注意:如果你session中保存的是非string类型的对象,需要重载toString方法。

4.5.6 principalCookieName配置

如果你的user信息保存在cookie中,你可以配置principalCookieName,使得druid知道当前的user是谁。

       <init-param>

            <param-name>principalCookieName</param-name>

            <param-value>xxx.user</param-value>

     </init-param>

根据需要,把其中的xxx.user修改为你user信息保存在cookie中的cookieName。

4.5.7 profileEnable配置

druid 0.2.7版本开始支持profile,配置profileEnable能够监控单个url调用的sql列表。

<init-param>

    <param-name>profileEnable</param-name>

    <param-value>true</param-value>

</init-param>

4.5.8 结果展示

4.6  配置ConfigFilter

ConfigFilter的作用包括:

l  从配置文件中读取配置

l  从远程http文件中读取配置

l  为数据库密码提供加密功能

4.6.1 配置ConfigFilter
4.6.1.1  配置文件从本地文件系统中读取

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

     init-method="init" destroy-method="close">

     <property name="filters" value="config" />

     <property name="connectionProperties" value="config.file=file:///home/admin/druid-pool.properties" />

</bean>

4.6.1.2  配置文件从远程http服务器中读取

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

     init-method="init" destroy-method="close">

     <property name="filters" value="config" />

     <property name="connectionProperties" value="config.file=http://127.0.0.1/druid-pool.properties" />

</bean>

这种配置方式,使得一个应用集群中,多个实例可以从同一个地方读取配置,集中配置,集中修改,部署更简单。

4.6.1.3 通过jvm启动参数来使用ConfigFilter

DruidDataSource支持jvm启动参数配置filters,所以你可以:

java -Ddruid.filters=config ....

4.6.2 数据库密码加密

数据库密码直接写在配置中,对运维安全来说,是一个很大的挑战。Druid为此提供一种数据库密码加密的手段ConfigFilter。

4.6.2.1 执行命令加密数据库密码

在命令行中执行如下命令:

java -cp druid-1.0.16.jar com.alibaba.druid.filter.config.ConfigTools you_password

输出:

privateKey:MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA6+4avFnQKP+O7bu5YnxWoOZjv3no4aFV558HTPDoXs6EGD0HP7RzzhGPOKmpLQ1BbA5viSht+aDdaxXp6SvtMQIDAQABAkAeQt4fBo4SlCTrDUcMANLDtIlax/I87oqsONOg5M2JS0jNSbZuAXDv7/YEGEtMKuIESBZh7pvVG8FV531/fyOZAiEA+POkE+QwVbUfGyeugR6IGvnt4yeOwkC3bUoATScsN98CIQDynBXC8YngDNwZ62QPX+ONpqCel6g8NO9VKC+ETaS87wIhAKRouxZL38PqfqV/WlZ5ZGd0YS9gA360IK8zbOmHEkO/AiEAsES3iuvzQNYXFL3x9Tm2GzT1fkSx9wx+12BbJcVD7AECIQCD3Tv9S+AgRhQoNcuaSDNluVrL/B/wOmJRLqaOVJLQGg==

publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOvuGrxZ0Cj/ju27uWJ8VqDmY7956OGhVeefB0zw6F7OhBg9Bz+0c84RjzipqS0NQWwOb4kobfmg3WsV6ekr7TECAwEAAQ==

password:PNak4Yui0+2Ft6JSoKBsgNPl+A033rdLhFw+L0np1o+HDRrCo9VkCuiiXviEMYwUgpHZUFxb2FpE0YmSguuRww==

输入你的数据库密码,输出的是加密后的结果。

4.6.2.2 配置数据源,提示Druid数据源需要对数据库密码进行解密

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"

     init-method="init" destroy-method="close">

     <property name="url" value="jdbc:derby:memory:spring-test;create=true" />

     <property name="username" value="sa" />

     <property name="password" value="${password}" />

     <property name="filters" value="config" />

     <property name="connectionProperties" value="config.decrypt=true;config.decrypt.key=${publickey}" />

</bean>

4.6.2.3 配置参数,让ConfigFilter解密密码

有三种方式配置:

l  可以在配置文件my.properties中指定config.decrypt=true

l  也可以在DruidDataSource的ConnectionProperties中指定config.decrypt=true

l  也可以在jvm启动参数中指定-Ddruid.config.decrypt=true

5.   其他配置说明

5.1 连接泄漏监测

当程序存在缺陷时,申请的连接忘记关闭,这时候,就存在连接泄漏了。Druid提供了RemoveAbandanded相关配置,用来关闭长时间不使用的连接。

5.1.1 配置RemoveAbandanded

  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">

     ... ...

     <property name="removeAbandoned" value="true" /> <!-- 打开removeAbandoned功能 -->

     <property name="removeAbandonedTimeout" value="1800" /> <!-- 1800秒,也就是30分钟 -->

     <property name="logAbandoned" value="true" /> <!-- 关闭abanded连接时输出错误日志 -->

     ... ...

  </bean>

配置removeAbandoned对性能会有一些影响,建议怀疑存在泄漏之后再打开。在上面的配置中,如果连接超过30分钟未关闭,就会被强行回收,并且日志记录连接申请时的调用堆栈。

5.1.2 内置监控页面查看未关闭连接堆栈信息

当removeAbandoned=true之后,可以在内置监控界面datasource.html中的查看ActiveConnectionStackTrace属性的,可以看到未关闭连接的具体堆栈信息,从而方便查出哪些连接泄漏了。

5.1.3 web应用

如果你的应用配置了WebStatFilter,在内置监控页面weburi-detail.html中,查看JdbcPoolConnectionOpenCount和JdbcPoolConnectionCloseCount属性,如果不相等,就是泄漏了。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值