Druid是一个非常好用的数据库连接池,但是他的好并不止体现在作为一个连接池加快数据访问性能上和连接管理上,他带有一个强大的监控工具:Druid Monitor。不仅可以监控数据源和慢查询,还可以监控Web应用、URI监控、Session监控、Spring监控。
开启Druid的监控功能,可以在应用运行期间,通过监控提供的多维度数据来分析使用数据库的运行情况,从而可以调整程序设计,以达到优化数据库访问性能的目的。接下来在DruidConfig配置类中定义一个监控服务器和一个过滤器,监控服务器设定了访问监控后台的连接地址为“/druid/*”,设定了访问数据库的白名单和黑名单,即通过访问者IP地址来控制访问来源,增加了数据库的安全设置,还设置了一个用来登录监控后台的账户和密码。
Druid首先是一个数据库连接池,号称是Java语言中最好的数据库连接池,能够提供强大的监控和扩展功能。在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。
1,Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。
2、可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。
3、数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。
4、SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。
5、扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。
@Configuration
public class DruidConfig {
/*
DruidConfig配置类中首先需要把DruidDataSource数据源加入到IOC容器中
*/
@ConfigurationProperties(prefix="spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控:ServletRegistrationBean
/**
* 配置监控服务器
*
* @return 返回监控注册的servlet对象
*/
@Bean
public ServletRegistrationBean statViewServlet(){
//下面这句代码是固定的ServletRegistrationBean对象的构造器有两个参数一个是StatViewServlet对象,另外一个
//是/druid/*参数,/druid/*是一个连接地址,如果监控服务器想要访问监控后台就可以访问此地址
//如localhost:8080/druid/,默认会访问login.html首页
ServletRegistrationBean bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
HashMap initParameters = new HashMap<>();
//后台需要有人登陆,账号密码配置
initParameters.put("loginUsername","admin");//登陆key是固定的loginUsername loginPassword
initParameters.put("loginPassword","123456");
// 添加IP白名单,默认""表示所有人都可以访问
initParameters.put("allow","");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
initParameters.put("deny","192.168.25.123");
bean.setInitParameters(initParameters);
return bean;
}
/**
* 配置服务过滤器
*
* @return 返回过滤器配置对象
*/
@Bean
public FilterRegistrationBean webStatFilter() {
//FilterRegistrationBean对象构造器有一个参数是WebStatFilter对象
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
Map initParameters=new HashMap<>();
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
initParameters.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
}
之前在ssm三大框架中配置servlet服务器和filter过滤器都是在web.xml文件中配置的,现在可以在Druid数据库连接池的配置类中通过把ServletRegistrationBean,FilterRegistrationBean加入到IOC容器中来实现servlet和filter的配置,如上面的配置类DruidServlet,servlet和filter都需要设置一些初始化的参数,如下图
点击setInitParameters源码如下图:
可以发现此方法需要一个Map
StatViewServlet类的作用包括:
此类相当于在web.xml文件中配置了如下信息:
1. <servlet>
2. <servlet-name>DruidStatView</servlet-name>
3. <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
4.
5. *<!-- StatViewSerlvet展示出来的监控信息比较敏感,是系统运行的内部情况,如果你需要做访问控制,可以配置allow来控制 -->*
6. <init-param>
7. <param-name>allow</param-name>
8. <param-value>127.0.0.0</param-value>
9. </init-param>
10.
11. *<!-- 在StatViewSerlvet输出的html页面中,有一个功能是Reset All,执行这个操作之后,会导致所有计数器清零,重新计数 -->*
12. <init-param>
13. <param-name>resetEnable</param-name>
14. <param-value>false</param-value>
15. </init-param>
16.
17. *<!-- 用户名及密码配置 -->*
18. <init-param>
19. *<!-- 用户名 -->*
20. <param-name>loginUsername</param-name>
21. <param-value>admin</param-value>
22. </init-param>
23. <init-param>
24. *<!-- 密码 -->*
25. <param-name>loginPassword</param-name>
26. <param-value>12345</param-value>
27. </init-param>
28. </servlet>
29. <servlet-mapping>
30. <servlet-name>DruidStatView</servlet-name>
31. <url-pattern>/druid/*
32.
因此现在你应该能够理解为什么在浏览器中输入localhost:8080/druid/,这个接口会跳转到Druid数据库连接池的监控后台了,因为在servlet-mapping中已经配置了url-pattern为/druid/*表示可以接收请求/druid,然后跳转到servlet-class标签中定义的类中,然后服务器进行一定的处理让其跳转到相关的html页面中,比如localhost:8080/druid/,会跳转到Druid数据库连接池监控后台的login.html页面。
此类相当于在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,*.jsp,/druid/*,/download/*
sessionStatMaxCount
2000
sessionStatEnable
true
principalSessionName
session_user_key
profileEnable
true
DruidWebStatFilter
/*
通过上面的配置可以理解在FilterRegistrationBean中代码的意义:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--引入druid数据源 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<!-- 如果 不加入这依赖 配置监控统计拦截的filters时 这个会报错 filters: stat,wall,log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
使用Druid数据库连接池的时候一定要同时导入log4j,如果不导入log4j依赖在启动项目的时候会出现下图错误
spring:
datasource:
username: root
password: wangqing
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.jdbc.Driver
#需要注意的是type可能根据版本的不同取不同的值
#type: com.alibaba.druid.pool.DruidDataSource
type: com.zaxxer.hikari.util.DriverDataSource
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
# 1)监控统计用的filter:stat
# 2)日志用的filter:log4j
# 3)防御sql注入的filter:wall
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 合并多个DruidDataSource的监控数据
#useGlobalDataSourceStat: true
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix="spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控:ServletRegistrationBean
/**
* 配置监控服务器
*
* @return 返回监控注册的servlet对象
*/
@Bean
public ServletRegistrationBean statViewServlet(){
//下面这句代码是固定的ServletRegistrationBean对象的构造器有两个参数一个是StatViewServlet对象,另外一个
//是/druid/*参数,/druid/*是一个连接地址,如果监控服务器想要访问监控后台就可以访问此地址
//如localhost:8080/druid/,默认会访问login.html首页
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
HashMap<String, String> initParameters = new HashMap<>();
//后台需要有人登陆,账号密码配置
initParameters.put("loginUsername","admin");//登陆key是固定的loginUsername loginPassword
initParameters.put("loginPassword","123456");
// 添加IP白名单,默认""表示所有人都可以访问
initParameters.put("allow","");
// 添加IP黑名单,当白名单和黑名单重复时,黑名单优先级更高
initParameters.put("deny","192.168.25.123");
bean.setInitParameters(initParameters);
return bean;
}
/**
* 配置服务过滤器
*
* @return 返回过滤器配置对象
*/
@Bean
public FilterRegistrationBean webStatFilter() {
//FilterRegistrationBean对象构造器有一个参数是WebStatFilter对象
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
Map<String,String> initParameters=new HashMap<>();
// 添加过滤规则
filterRegistrationBean.addUrlPatterns("/*");
// 忽略过滤格式
initParameters.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*,");
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
}
通过jdbcTemplate执行sql语句,然后去Druid数据库连接池的监控后台看看数据库的运行情况,监控后台的接口地址是
localhost:8080/druid/login.html如下图
然后我们在ServletRegistrationBean中配置的用户名是admin,密码是123456然后登陆进去,如下图
因此Druid是一个非常好用的数据库连接池,但是他的好并不止体现在作为一个连接池加快数据访问性能上和连接管理上,他带有一个强大的监控工具:Druid Monitor。不仅可以监控数据源和慢查询,还可以监控Web应用、URI监控、Session监控、Spring监控。
DRUID 属性说明表
属性(Parameter) | 默认值(Default) | 描述(Description) |
---|---|---|
username | 连接数据库的用户名 | |
password | 连接数据库的密码 | |
jdbcUrl | 同DBCP中的jdbcUrl属性 | |
driverClassName | 根据url自动识别 | 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 *参见DBCP中的initialSize属性 |
maxActive | 8 | 最大连接池数量(Maximum number of Connections a pool will maintain at any given time.) *参见DBCP中的maxTotal属性 |
maxIdle | 8 | 已经不再使用,配置了也没效果*参见DBCP中的maxIdle属性 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedState- ments | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。 |
maxOpenPrepared- Statements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、 testWhileIdle都不会其作用。在mysql中通常为select ‘x’,在oracle中通常为select 1 from dual | |
timeBetweenEviction-RunsMillis | 1) Destroy线程会检测连接的间隔时间 2) testWhileIdle的判断依据 | |
minEvictableIdle- TimeMillis | Destory线程中如果检测到当前连接的最后活跃时间和当前时间的差值大于minEvictableIdleTimeMillis,则关闭当前连接。 | |
removeAbandoned | 对于建立时间超过removeAbandonedTimeout的连接强制关闭 | |
removeAbandoned-Timeout | 指定连接建立多长时间就需要被强制关闭 | |
logAbandoned | false | 指定发生removeabandoned的时候,是否记录当前线程的堆栈信息到日志中 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 1)监控统计用的filter:stat 2)日志用的filter:log4j 3)防御sql注入的filter:wall |
**1. 基本原理:**在内部对象池中,维护一定数量的数据库连接,并对外暴露数据库连接的获取和返回方法。
如外部使用者可通过getConnection方法获取数据库连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时的连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
2.作用
①资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,增进了系统环境的平稳性(减少内存碎片以级数据库临时进程、线程的数量)
②更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池内备用。此时连接池的初始化操作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
③新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接技术。
④统一的连接管理,避免数据库连接泄露
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用的连接,从而避免了常规数据库连接操作中可能出现的资源泄露
java的所有的连接池 无论是c3p0、dbcp还是druid,都有一个类似maxWait或者maxIdleTime配置项。具体含义就是当连接长时间没有向服务器发请求的时候,断开这个连接,避免对数据库连接的浪费。这个时间不是随便设的,它的依据是数据库的连接最大空闲时间。连接的空闲时间可以理解成虽然连接池里的连接被拿了出来并且也连接着具体的数据库,但是此连接长时间没有向服务器发送请求。因此连接池有一个机制就是当从连接池中取出一个连接并且连接上了具体的数据库,如果此连接长时间没有向服务器发送请求,就把这个连接断开。
数据库连接遗漏就是数据库连接取了,连接也连接到相应的数据库进行了一系列的操作,但是操作完之后没有立即把此连接释放到连接池中,如果连接连接数据库的时候进行了一系列的操作,这时会给连接标记为忙状态,然后等这一系列操作时候,此连接已经使用完毕,会给此连接标记成是空闲状态,如果数据池里的连接一直是处于空闲状态也就是此连接确实连接数据库但就是不进行相关的数据库操作,也即是俗话说的占着茅抗不拉屎也不让别的数据库连接,这样就会大大影响数据库的性能问题,因为这个时候服务器就会根据配置参数在池中创建一定数量的连接,这是很费性能的,因此数据库连接池为了避免连接遗漏这种情况的发生,会释放空闲时间超过最大空闲时间的数据库连接,也即是把占着茅抗不拉屎的空闲连接全都释放掉,也即是调用releaseConnection方法把连接着具体数据库但长时间不操作的连接重新变成可以被其它数据库连接的连接。
在J2EE中,服务器在启动时会创建一定数量的池连接,并一直维持不少于此数目的连接池。当客户程序需要访问数据库时,就可以直接从池中获取与数据库的连接(获取一个空闲的连接),而不用去创建一个新的连接,同时将该连接标记为忙状态。当使用完毕后再把该连接标记为空闲状态,这样其他用户就可以使用这个连接了。如果当前没有空闲的连接,那么服务器就会根据配置参数在池中创建一定数量的连接。采用这种方法对数据库连接进行管理后可以大幅缩短用户的响应时间,提高运行效率。另一方面,为了提高数据库操作的性能,数据库连接池会释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。
总之如果连接池中的连接连着相关的数据库但是不进行操作,这时这个连接就处于一个空闲状态。而这种空闲状态之所以会发生一般都是因为用户在用完连接之后忘记释放资源了,因此导致了连接池中的连接一直连接着数据库但不进行相关操作这一情况的发生,这种情况也叫做数据库连接遗漏。所以数据库的连接池有一个机制就是会主动释放空闲时间大于最大时间的空闲时间的连接,让这个连接重新变成一个其它数据库可以用的连接。