【轉(zhuǎn)自:https://developer.aliyun.com/article/1157595?accounttraceid=be1e563c5e6343acb1b5556c40306748kjzb】
概述
簡介
Java 程序很大一部分要操作數(shù)據(jù)庫,為了提高性能操作數(shù)據(jù)庫的時候,又不得不使用數(shù)據(jù)庫連接池。
- Druid 是阿里巴巴開源平臺上一個數(shù)據(jù)庫連接池實現(xiàn),結(jié)合了 C3P0、DBCP 等 DB 池的優(yōu)點,同時加入了日志監(jiān)控。
- Druid 可以很好的監(jiān)控 DB 池連接和 SQL 的執(zhí)行情況,天生就是針對監(jiān)控而生的 DB 連接池。
- Druid 已經(jīng)在阿里巴巴部署了超過600個應(yīng)用,經(jīng)過一年多生產(chǎn)環(huán)境大規(guī)模部署的嚴苛考驗。
- Spring Boot 2.0 以上默認使用 Hikari 數(shù)據(jù)源,Hikari 與 Driud 都是當(dāng)前 Java Web 上最優(yōu)秀的數(shù)據(jù)源。
- Druid 的 Github地址
Druid 基本配置參數(shù)介紹
com.alibaba.druid.pool.DruidDataSource 基本配置參數(shù)
-
name :數(shù)據(jù)源名稱
如果存在多個數(shù)據(jù)源,監(jiān)控的時候可以通過名字來區(qū)分開來
如果沒有配置,將會生成一個名字,格式是"DataSource-"+System.identityHashCode(this)
jdbcUrl :連接數(shù)據(jù)庫的 url,不同數(shù)據(jù)庫不一樣
username :連接數(shù)據(jù)庫的用戶名
password :連接數(shù)據(jù)庫的密碼
-
driverClassName :數(shù)據(jù)庫驅(qū)動類
可配可不配,如果不配置 druid 會根據(jù) url 自動識別 dbType,然后選擇相應(yīng)的 driverClassName(建議配置下)
initialSize :初始化時建立物理連接的個數(shù),初始化發(fā)生在顯示調(diào)用 init 方法,或者第一次 getConnection 時
maxActive :最大連接池數(shù)量
maxIdle :已經(jīng)不再使用,配置了也沒效果
minIdle :最小連接池數(shù)量
-
maxWait :獲取連接時最大等待時間,單位毫秒
配置了 maxWait 之后,缺省啟用公平鎖,并發(fā)效率會有所下降(可以通過配置 useUnfairLock=true 使用非公平鎖)
-
poolPreparedStatements :是否緩存 preparedStatement,即 PsCache
PSCache 對支持游標的數(shù)據(jù)庫性能提升巨大,比如說 oracle,而 mysql 則建議關(guān)閉
-
maxOpenPreparedStatements :要啟用 PSCache,必須配置大于0
當(dāng)大于 0 時,poolPreparedStatements 自動觸發(fā)修改為 true
在 Druid 中,不會存在 Oracle 下 PSCache 占用內(nèi)存過多的問題,可以把這個數(shù)值配置大一點,比如 100
-
validationQuery :用來檢測連接是否有效的 sql,要求是一個查詢語句
如果 validationQuery 為null,testOnBorrow、testOnReturn 、testWhileIdle 都不會起作用
testOnBorrow :申請連接時執(zhí)行 validationQuery 檢測連接是否有效,做了這個配置會降低性能
testOnReturn :歸還連接時執(zhí)行 validationQuery 檢測連接是否有效,做了這個配置會降低性能
-
testWhileIdle :建議配置為 true,不影響性能,并且保證安全性
申請連接的時候檢測,如果空閑時間大于 timeBetweenEvictionRunMills,執(zhí)行 validationQuery 檢測連接是否有效
-
timeBetweenEvictionRunMillis :間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
- Destory 線程會檢測連接的間隔時間
- testWhileIdle 的判斷依據(jù)(詳見 testWhileIdele 屬性的說明)
numTestsPerEvictionRun :廢棄,一個 DruidDataSource 只支持一個 EvicationRun
minEvictableIdleTimeMillis :一個連接在池中最小生存的時間,單位是毫秒
connectionInitSqls :物理連接初始化的時候執(zhí)行 sql
exceptionSorter :當(dāng)數(shù)據(jù)庫拋出一些不可恢復(fù)的異常時,拋棄連接
-
filters :通過別名的方式配置擴展插件,屬性類型是字符串
常用的插件有:監(jiān)控統(tǒng)計用的 filter(stat:監(jiān)控統(tǒng)計,log:4:日志記錄,wall:防御sql注入)
proxyFilters :類型是 List<com.alibaba.druid,filter.Filter>,如果同時配置 filter 和 proxyFilters,是組合關(guān)系(并非)
Druid 集成方式(含依賴)
Spring Boot 方式(推薦)
依賴
<!--引入druid數(shù)據(jù)源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
yml 配置文件切換數(shù)據(jù)源
-
方式1:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot_test?characterEncoding=utf8&serverTimezone=UTC username: root password: root # druid-spring-boot-starter 依賴自動生效 druid,可以不配置 type 屬性,但建議配置 type: com.alibaba.druid.pool.DruidDataSource -
方式2:druid 專用配置(需要 druid-spring-boot-starter 依賴)
spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/springboot_test?characterEncoding=utf8&serverTimezone=UTC username: root password: root
Spring 方式
依賴
<!--引入druid數(shù)據(jù)源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
yml 配置文件切換數(shù)據(jù)源
Spring Boot 2.0 以上默認使用 com.zaxxer.hikari.HikariDataSource 數(shù)據(jù)源,但可以通過 spring.datasource.type 指定數(shù)據(jù)源。
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://192.168.10.132:3306/testdb
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# druid 數(shù)據(jù)源專有配置
# 不是druid-spring-boot-starter依賴,SpringBoot默認是不注入druid數(shù)據(jù)源專有屬性值的,需要自己綁定
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
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計。stat:監(jiān)控統(tǒng)計 log4:日志記錄 wall:防御sql注入
# 如果運行時報錯:ClassNotFoundException:orgapache.log4j.Priority,則導(dǎo)入log4j依賴即可
filters: stat,wall,log4j
# 自動往數(shù)據(jù)庫建表
#schema:
#- classpath:department.sql
配置類:添加 DruidDataSource 組件到容器中,并綁定屬性
@Configuration
public class DruidDataSourceConfig {
/**
* 添加 DruidDataSource 組件到容器中,并綁定屬性
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource")
public DataSource druid(){
return new DruidDataSource();
}
}
Druid 密碼回調(diào)
現(xiàn)在很多項目都是把數(shù)據(jù)庫的密碼明文放在配置文件中,這樣其實是不安全的,應(yīng)該將密碼加密后再放到配置中,這樣可以一定程度的保護數(shù)據(jù)庫密碼的安全。
Druid 可以通過配置參數(shù) passwordCallBack 來指定一個密碼接口回調(diào)類進行密文密碼解密操作。
實現(xiàn)步驟:
-
自定義一個密碼接口回調(diào)類
需要實現(xiàn) DruidPasswordCallback 接口并重寫接口方法 setProperties()
import com.alibaba.druid.util.DruidPasswordCallback; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.Properties; @Slf4j public class DruidCustomPasswordCallback extends DruidPasswordCallback { @Override public void setProperties(Properties properties) { super.setProperties(properties); // 獲取配置文件中的已經(jīng)加密的密碼(spring.datasource.druid.connect-properties.password) String pwd = (String)properties.get("password"); if (StringUtils.isNotEmpty(pwd)) { try { // 這里的代碼是將密碼進行解密,并設(shè)置 String password = "解密后的明文密碼"; setPassword(password.toCharArray()); } catch (Exception e) { e.printStackTrace(); } } } } -
yml 配置文件中配置密碼接口回調(diào)類
注:
-
可以通過配置文件中的 password-callback-class-name 或 password-callback 屬性來配置密碼接口回調(diào)類
推薦使用 spring.datasource.druid.password-callback-class-name
若是使用 spring.datasource.druid.password-callback 屬性,則需要搭配相應(yīng)的類型轉(zhuǎn)換器才能用,不然報錯:
No converter found capable of converting from type [java.lang.String] to type [javax.security.auth.callback.PasswordCallback] -
根據(jù)密碼接口回調(diào)類中的密文密碼解密的邏輯,必須配置 spring.datasource.druid.connect-properties.password 屬性才會進行密文密碼解密操作并重置密碼為解密后的明文密碼
若不配置 connect-properties.password ,則默認使用的 spring.datasource(.druid).password 的屬性值
若是 Spring 集成 Druid,則可以在初始化 DruidDatabaseSource 時,手動配置其 passwordCallBack 屬性值
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/leyou?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: root11 # type: com.alibaba.druid.pool.DruidDataSource druid: # 配置密碼接口回調(diào)類的全限定類名 password-callback-class-name: com.duran.ssmtest.config.DruidCustomPasswordCallback # 配置密碼接口回調(diào)類中方法入?yún)roperties的值(自定義Map<String, String>) connect-properties: # 配置密文密碼(傳參到配置密碼接口回調(diào)類中解密) password: aaa -
Druid 數(shù)據(jù)源監(jiān)控
Druid 數(shù)據(jù)源具有監(jiān)控的功能,并提供了一個 web 界面方便用戶查看。
-
設(shè)置 Druid 的后臺管理頁面,比如 登錄賬號、密碼 等;配置后臺管理
import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @Configuration public class DruidDataSourceConfig { /** * 添加 DruidDataSource 組件到容器中,并綁定屬性 */ @Bean @ConfigurationProperties(prefix = "spring.datasource") @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource") public DataSource druid(){ return new DruidDataSource(); } /** * 配置 Druid 監(jiān)控管理后臺的Servlet; * 內(nèi)置 Servlet 容器時沒有web.xml文件,所以使用 Spring Boot 的注冊 Servlet 方式 */ @Bean @ConditionalOnClass(DruidDataSource.class) public ServletRegistrationBean statViewServlet(){ // 這些參數(shù)可以在 http.StatViewServlet 的父類 ResourceServlet 中找到 Map<String,String> initParams = new HashMap<>(); initParams.put("loginUsername","admin"); initParams.put("loginPassword","123456"); // allow:Druid 后臺允許誰可以訪問。默認就是允許所有訪問。 initParams.put("allow",""); // 后面參數(shù)為空則所有人都能訪問,一般會寫一個具體的ip或ip段 // deny:Druid 后臺禁止誰能訪問 // initParams.put("deny","192.168.10.132"); // 注冊一個servlet,同時表明/druid/* 這個請求會走到這個servlet,而druid內(nèi)置了這個請求的接收 ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); bean.setInitParameters(initParams); return bean; } /** * 配置一個web監(jiān)控的filter */ @Bean @ConditionalOnClass(DruidDataSource.class) public FilterRegistrationBean webStatFilter(){ Map<String,String> initParams = new HashMap<>(); // 這些不進行統(tǒng)計 initParams.put("exclusions","*.js,*.css,/druid/*"); FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); bean.setInitParameters(initParams); bean.setUrlPatterns(Arrays.asList("/*")); return bean; } } 運行應(yīng)用,瀏覽器訪問 :http://localhost:8080/druid/login.html