數(shù)據(jù)庫(kù)連接池為什么首選Druid
Spring Boot 作為主流微服務(wù)框架,擁有成熟的社區(qū)生態(tài)。市場(chǎng)應(yīng)用廣泛,為了方便大家,整理了一個(gè)基于spring boot的常用中間件快速集成入門系列手冊(cè),涉及RPC、緩存、消息隊(duì)列、分庫(kù)分表、注冊(cè)中心、分布式配置等常用開(kāi)源組件,大概有幾十篇文章,陸續(xù)會(huì)開(kāi)放出來(lái),感興趣同學(xué)可以關(guān)注&收藏
1、簡(jiǎn)介
Druid是阿里巴巴的一個(gè)開(kāi)源項(xiàng)目,號(hào)稱為監(jiān)控而生的數(shù)據(jù)庫(kù)連接池,在功能、性能、擴(kuò)展性方面都超過(guò)其他,例如 DBCP、C3P0、BoneCP、Proxool、JBoss、DataSource 等連接池,而且Druid已經(jīng)在阿里巴巴部署了超過(guò)600個(gè)應(yīng)用,通過(guò)了極為嚴(yán)格的考驗(yàn),這才收獲了大家的青睞!
Druid是一個(gè)JDBC組件,包含三個(gè)部分:
DruidDriver 代理Driver,能夠提供基于Filter-Chain模式的插件體系。 DruidDataSource 高效管理的數(shù)據(jù)庫(kù)連接池。 SQLParser SQL語(yǔ)法分析
強(qiáng)大的監(jiān)控特性
Druid內(nèi)置了一個(gè)功能強(qiáng)大的StatFilter插件可以監(jiān)控?cái)?shù)據(jù)庫(kù)訪問(wèn)性能,可以清楚知道連接池和SQL的工作情況。
監(jiān)控SQL的執(zhí)行時(shí)間、ResultSet持有時(shí)間、返回行數(shù)、更新行數(shù)、錯(cuò)誤次數(shù)、錯(cuò)誤堆棧信息。
SQL執(zhí)行的耗時(shí)區(qū)間分布。什么是耗時(shí)區(qū)間分布呢?比如說(shuō),某個(gè)SQL執(zhí)行了1000次,其中0-1毫秒?yún)^(qū)間50次,1-10毫秒800次,10-100毫秒100次,100-1000毫秒30次,1-10秒15次,10秒以上5次。通過(guò)耗時(shí)區(qū)間分布,能夠非常清楚知道SQL的執(zhí)行耗時(shí)情況。
監(jiān)控連接池的物理連接創(chuàng)建和銷毀次數(shù)、邏輯連接的申請(qǐng)和關(guān)閉次數(shù)、非空等待次數(shù)、PSCache命中率等。
數(shù)據(jù)庫(kù)密碼加密
直接把數(shù)據(jù)庫(kù)密碼寫在配置文件中,容易導(dǎo)致安全問(wèn)題。DruidDriver和DruidDataSource都支持PasswordCallback。
SQLParser
SQL Parser是Druid的一個(gè)重要組成部分,它提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支持,這是一個(gè)手寫的高性能SQL Parser,支持Visitor模式,使得分析SQL的抽象語(yǔ)法樹(shù)很方便。簡(jiǎn)單SQL語(yǔ)句用時(shí)10微秒以內(nèi),復(fù)雜SQL用時(shí)30微秒。
通過(guò)Druid提供的SQL Parser可以在JDBC層攔截SQL做相應(yīng)處理,比如防御SQL注入(WallFilter)、合并統(tǒng)計(jì)沒(méi)有參數(shù)化的SQL(StatFilter的mergeSql)、SQL格式化、分庫(kù)分表。
2、依賴集成
在pom.xml中引入druid官方提供的Spring Boot Starter組件
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
application.yaml 配置文件配置數(shù)據(jù)庫(kù)信息、以及Druid的連接池
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ds0?characterEncoding=utf-8&useSSL=false
username: root
password: 111111
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: -1
use-global-data-source-stat: true
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall,log4j
3、Druid 配置詳解
| 配置 | 缺省值 | 說(shuō)明 |
|---|---|---|
| name | 如果存在多個(gè)數(shù)據(jù)源,監(jiān)控的時(shí)候可以通過(guò)名字來(lái)區(qū)分開(kāi)來(lái)。如果沒(méi)有配置,將會(huì)生成一個(gè)名字,格式是:”DataSource-“ + System.identityHashCode(this) | |
| url | 連接數(shù)據(jù)庫(kù)的url,不同數(shù)據(jù)庫(kù)不一樣 | |
| username | 連接數(shù)據(jù)庫(kù)的用戶名 | |
| password | 連接數(shù)據(jù)庫(kù)的密碼。如果你不希望密碼直接寫在配置文件中,可以使用ConfigFilter | |
| driverClassName | 根據(jù)url自動(dòng)識(shí)別 ,這一項(xiàng)可配可不配,如果不配置druid會(huì)根據(jù)url自動(dòng)識(shí)別dbType,然后選擇相應(yīng)的driverClassName | |
| initialSize | 0 | 初始化時(shí)建立物理連接的個(gè)數(shù)。初始化發(fā)生在顯示調(diào)用init方法,或者第一次getConnection時(shí) |
| maxActive | 8 | 最大連接池?cái)?shù) |
| minIdle | 最小連接池?cái)?shù) | |
| maxWait | 獲取連接時(shí)最大等待時(shí)間,單位毫秒。配置了maxWait之后,缺省啟用公平鎖,并發(fā)效率會(huì)有所下降,如果需要可以通過(guò)配置useUnfairLock屬性為true使用非公平鎖。 | |
| poolPreparedStatements | false | 是否緩存preparedStatement,也就是PSCache。PSCache對(duì)支持游標(biāo)的數(shù)據(jù)庫(kù)性能提升巨大,比如說(shuō)oracle。在mysql下建議關(guān)閉。 |
| maxPoolPreparedStatementPerConnectionSize | -1 | 要啟用PSCache,必須配置大于0,當(dāng)大于0時(shí),poolPreparedStatements自動(dòng)觸發(fā)修改為true。在Druid中,不會(huì)存在Oracle下PSCache占用內(nèi)存過(guò)多的問(wèn)題,可以把這個(gè)數(shù)值配置大一些,比如說(shuō)100 |
| validationQuery | 用來(lái)檢測(cè)連接是否有效的sql,要求是一個(gè)查詢語(yǔ)句,常用select ‘x’。如果validationQuery為null,testOnBorrow、testOnReturn、testWhileIdle都不會(huì)起作用。 | |
| validationQueryTimeout | 單位:秒,檢測(cè)連接是否有效的超時(shí)時(shí)間。底層調(diào)用jdbc Statement對(duì)象的void setQueryTimeout(int seconds)方法 | |
| testOnBorrow | true | 申請(qǐng)連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效,做了這個(gè)配置會(huì)降低性能。 |
| testOnReturn | false | 歸還連接時(shí)執(zhí)行validationQuery檢測(cè)連接是否有效,做了這個(gè)配置會(huì)降低性能。 |
| testWhileIdle | false | 建議配置為true,不影響性能,并且保證安全性。申請(qǐng)連接的時(shí)候檢測(cè),如果空閑時(shí)間大于timeBetweenEvictionRunsMillis,執(zhí)行validationQuery檢測(cè)連接是否有效。 |
| keepAlive | false (1.0.28) | 連接池中的minIdle數(shù)量以內(nèi)的連接,空閑時(shí)間超過(guò)minEvictableIdleTimeMillis,則會(huì)執(zhí)行keepAlive操作。 |
| timeBetweenEvictionRunsMillis | 1分鐘(1.0.14) | 有兩個(gè)含義:1) Destroy線程會(huì)檢測(cè)連接的間隔時(shí)間,如果連接空閑時(shí)間大于等于minEvictableIdleTimeMillis則關(guān)閉物理連接。2) testWhileIdle的判斷依據(jù),詳細(xì)看testWhileIdle屬性的說(shuō)明 |
| numTestsPerEvictionRun | 30分鐘(1.0.14) | 不再使用,一個(gè)DruidDataSource只支持一個(gè)EvictionRun |
| minEvictableIdleTimeMillis | 連接保持空閑而不被驅(qū)逐的最小時(shí)間 | |
| connectionInitSqls | 物理連接初始化的時(shí)候執(zhí)行的sql | |
| exceptionSorter | 根據(jù)dbType自動(dòng)識(shí)別 | 當(dāng)數(shù)據(jù)庫(kù)拋出一些不可恢復(fù)的異常時(shí),拋棄連接 |
| filters | 屬性類型是字符串,通過(guò)別名的方式配置擴(kuò)展插件,常用的插件有:監(jiān)控統(tǒng)計(jì)用的filter:stat, 日志用的filter:log4j , 防御sql注入的filter:wall | |
| proxyFilters | 類型是List,如果同時(shí)配置了filters和proxyFilters,是組合關(guān)系,并非替換關(guān)系 |
Druid提供以下幾種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、Druid 監(jiān)控
支持多種形式,本文列舉的是java類創(chuàng)建Bean實(shí)例方式。
@Bean
public ServletRegistrationBean druidServlet() {
logger.info("init Druid Servlet Configuration ");
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("loginUsername", "admin");// 用戶名
initParameters.put("loginPassword", "admin");// 密碼
initParameters.put("resetEnable", "false");// 禁用HTML頁(yè)面上的“Reset All”功能
initParameters.put("allow", ""); // IP白名單 (沒(méi)有配置或者為空,則允許所有訪問(wèn))
//initParameters.put("deny", "192.168.20.38");// IP黑名單 (存在共同時(shí),deny優(yōu)先于allow)
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
也可以采用另一種方式,在application.properties中添加Druid的監(jiān)控配置
# druid連接池監(jiān)控
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin
# 配置 StatFilter
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=2000
# 排除一些靜態(tài)資源,以提高效率
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
配置完成后, 并完成相關(guān)的數(shù)據(jù)庫(kù)操作配置, 啟動(dòng)Spring Boot應(yīng)用程序。
訪問(wèn)Druid監(jiān)控后臺(tái)頁(yè)面:http://127.0.0.1:8090/druid/sql.html ,
首先進(jìn)入登錄頁(yè),賬號(hào)和密碼在上面的配置項(xiàng)中

輸入用戶名:admin,密碼:admin,可以看到詳細(xì)的監(jiān)控頁(yè)面

首頁(yè)會(huì)展示項(xiàng)目使用的 JDK 版本、數(shù)據(jù)庫(kù)驅(qū)動(dòng)、JVM 相關(guān)統(tǒng)計(jì)信息。根據(jù)上面的菜單可以看出 Druid 的功能非常強(qiáng)大,支持?jǐn)?shù)據(jù)源、SQL 監(jiān)控、SQL 防火墻、URI 監(jiān)控等很多功能。
我們這里重點(diǎn)介紹一下 SQL 監(jiān)控,具體的展示信息如下:

這里的 SQL 監(jiān)控會(huì)將項(xiàng)目中具體執(zhí)行的 SQL 打印出來(lái),展示此 SQL 執(zhí)行了多少次、每次返回多少數(shù)據(jù)、執(zhí)行的時(shí)間分布是什么。這些功能非常的實(shí)用,方便我們?cè)趯?shí)際生產(chǎn)中查找出慢 SQL,最后對(duì) SQL 進(jìn)行調(diào)優(yōu)。
5、項(xiàng)目源碼地址
https://github.com/aalansehaiyang/spring-boot-bulking
模塊:spring-boot-bulking-druid
歡迎關(guān)注微信公眾號(hào):互聯(lián)網(wǎng)全棧架構(gòu),收取更多有價(jià)值的信息。
