配置Druid數(shù)據(jù)源
這一節(jié)的實(shí)踐我們將基于《JdbcTemplate訪問(wèn)MySQL數(shù)據(jù)庫(kù)》一文的代碼基礎(chǔ)上進(jìn)行。所以,讀者可以從文末的代碼倉(cāng)庫(kù)中,檢出chapter3-1
目錄來(lái)進(jìn)行下面的實(shí)踐學(xué)習(xí)。
下面我們就來(lái)開(kāi)始對(duì)Spring Boot項(xiàng)目配置Druid數(shù)據(jù)源:
第一步:在pom.xml
中引入druid官方提供的Spring Boot Starter封裝。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
第二步:在application.properties
中配置數(shù)據(jù)庫(kù)連接信息。
Druid的配置都以spring.datasource.druid
作為前綴,所以根據(jù)之前的配置,稍作修改即可:
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test
spring.datasource.druid.username=root
spring.datasource.druid.password=
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
第三步:配置Druid的連接池。
與Hikari一樣,要用好一個(gè)數(shù)據(jù)源,就要對(duì)其連接池做好相應(yīng)的配置,比如下面這樣:
spring.datasource.druid.initialSize=10
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.minIdle=1
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxOpenPreparedStatements=20
spring.datasource.druid.validationQuery=SELECT 1
spring.datasource.druid.validation-query-timeout=500
spring.datasource.druid.filters=stat
關(guān)于Druid中各連接池配置的說(shuō)明可查閱下面的表格:
配置 | 缺省值 | 說(shuō)明 |
---|---|---|
name | 配置這個(gè)屬性的意義在于,如果存在多個(gè)數(shù)據(jù)源,監(jiān)控的時(shí)候可以通過(guò)名字來(lái)區(qū)分開(kāi)來(lái)。如果沒(méi)有配置,將會(huì)生成一個(gè)名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此屬性至少在1.0.5版本中是不起作用的,強(qiáng)行設(shè)置name會(huì)出錯(cuò)。詳情-點(diǎn)此處open in new window。 | |
url | 連接數(shù)據(jù)庫(kù)的url,不同數(shù)據(jù)庫(kù)不一樣。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 連接數(shù)據(jù)庫(kù)的用戶(hù)名 | |
password | 連接數(shù)據(jù)庫(kù)的密碼。如果你不希望密碼直接寫(xiě)在配置文件中,可以使用ConfigFilter。詳細(xì)看這里open in new window | |
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ù)量 |
maxIdle | 8 | 已經(jīng)不再使用,配置了也沒(méi)效果 |
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è)查詢(xún)語(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ù)量以?xún)?nèi)的連接,空閑時(shí)間超過(guò)minEvictableIdleTimeMillis,則會(huì)執(zhí)行keepAlive操作。 |
timeBetweenEvictionRunsMillis | 1分鐘(1.0.14) | 有兩個(gè)含義: 1) Destroy線(xiàn)程會(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 | 屬性類(lèi)型是字符串,通過(guò)別名的方式配置擴(kuò)展插件,常用的插件有: 監(jiān)控統(tǒng)計(jì)用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall | |
proxyFilters | 類(lèi)型是List<com.alibaba.druid.filter.Filter>,如果同時(shí)配置了filters和proxyFilters,是組合關(guān)系,并非替換關(guān)系 |
到這一步,就已經(jīng)完成了將Spring Boot的默認(rèn)數(shù)據(jù)源HikariCP切換到Druid的所有操作。
#配置Druid監(jiān)控
既然用了Druid,那么對(duì)于Druid的監(jiān)控功能怎么能不用一下呢?下面就來(lái)再進(jìn)一步做一些配置,來(lái)啟用Druid的監(jiān)控。
第一步:在pom.xml
中引入spring-boot-starter-actuator
模塊
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
第二步:在application.properties
中添加Druid的監(jiān)控配置。
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin
上面的配置主要用于開(kāi)啟stat監(jiān)控統(tǒng)計(jì)的界面以及監(jiān)控內(nèi)容的相關(guān)配置,具體釋意如下:
spring.datasource.druid.stat-view-servlet.url-pattern
:訪問(wèn)地址規(guī)則spring.datasource.druid.stat-view-servlet.reset-enable
:是否允許清空統(tǒng)計(jì)數(shù)據(jù)spring.datasource.druid.stat-view-servlet.login-username
:監(jiān)控頁(yè)面的登錄賬戶(hù)spring.datasource.druid.stat-view-servlet.login-password
:監(jiān)控頁(yè)面的登錄密碼
第三步:針對(duì)之前實(shí)現(xiàn)的UserService
內(nèi)容,我們創(chuàng)建一個(gè)Controller來(lái)通過(guò)接口去調(diào)用數(shù)據(jù)訪問(wèn)操作:
@Data
@AllArgsConstructor
@RestController
public class UserController {
private UserService userService;
@PostMapping("/user")
public int create(@RequestBody User user) {
return userService.create(user.getName(), user.getAge());
}
@GetMapping("/user/{name}")
public List<User> getByName(@PathVariable String name) {
return userService.getByName(name);
}
@DeleteMapping("/user/{name}")
public int deleteByName(@PathVariable String name) {
return userService.deleteByName(name);
}
@GetMapping("/user/count")
public int getAllUsers() {
return userService.getAllUsers();
}
@DeleteMapping("/user/all")
public int deleteAllUsers() {
return userService.deleteAllUsers();
}
}
第四步:完成上面所有配置之后,啟動(dòng)應(yīng)用,訪問(wèn)Druid的監(jiān)控頁(yè)面http://localhost:8080/druid/
,可以看到如下登錄頁(yè)面:
輸入上面spring.datasource.druid.stat-view-servlet.login-username
和spring.datasource.druid.stat-view-servlet.login-password
配置的登錄賬戶(hù)與密碼,就能看到如下監(jiān)控頁(yè)面:
進(jìn)入到這邊時(shí)候,就可以看到對(duì)于應(yīng)用端而言的各種監(jiān)控?cái)?shù)據(jù)了。這里講解幾個(gè)最為常用的監(jiān)控頁(yè)面:
數(shù)據(jù)源:這里可以看到之前我們配置的數(shù)據(jù)庫(kù)連接池信息以及當(dāng)前使用情況的各種指標(biāo)。
SQL監(jiān)控:該數(shù)據(jù)源中執(zhí)行的SQL語(yǔ)句極其統(tǒng)計(jì)數(shù)據(jù)。在這個(gè)頁(yè)面上,我們可以很方便的看到當(dāng)前這個(gè)Spring Boot都執(zhí)行過(guò)哪些SQL,這些SQL的執(zhí)行頻率和執(zhí)行效率也都可以清晰的看到。如果你這里沒(méi)看到什么數(shù)據(jù)?別忘了我們之前創(chuàng)建了一個(gè)Controller,用這些接口可以觸發(fā)UserService對(duì)數(shù)據(jù)庫(kù)的操作。所以,這里我們可以通過(guò)調(diào)用接口的方式去觸發(fā)一些操作,這樣SQL監(jiān)控頁(yè)面就會(huì)產(chǎn)生一些數(shù)據(jù):
圖中監(jiān)控項(xiàng)上,執(zhí)行時(shí)間、讀取行數(shù)、更新行數(shù)都通過(guò)區(qū)間分布的方式表示,將耗時(shí)分布成8個(gè)區(qū)間:
- 0 - 1 耗時(shí)0到1毫秒的次數(shù)
- 1 - 10 耗時(shí)1到10毫秒的次數(shù)
- 10 - 100 耗時(shí)10到100毫秒的次數(shù)
- 100 - 1,000 耗時(shí)100到1000毫秒的次數(shù)
- 1,000 - 10,000 耗時(shí)1到10秒的次數(shù)
- 10,000 - 100,000 耗時(shí)10到100秒的次數(shù)
- 100,000 - 1,000,000 耗時(shí)100到1000秒的次數(shù)
- 1,000,000 - 耗時(shí)1000秒以上的次數(shù)
記錄耗時(shí)區(qū)間的發(fā)生次數(shù),通過(guò)區(qū)分分布,可以很方便看出SQL運(yùn)行的極好、普通和極差的分布。 耗時(shí)區(qū)分分布提供了“執(zhí)行+RS時(shí)分布”,是將執(zhí)行時(shí)間+ResultSet持有時(shí)間合并監(jiān)控,這個(gè)能方便診斷返回行數(shù)過(guò)多的查詢(xún)。
SQL防火墻:該頁(yè)面記錄了與SQL監(jiān)控不同維度的監(jiān)控?cái)?shù)據(jù),更多用于對(duì)表訪問(wèn)維度、SQL防御維度的統(tǒng)計(jì)。
該功能數(shù)據(jù)記錄的統(tǒng)計(jì)需要在spring.datasource.druid.filters
中增加wall
屬性才會(huì)進(jìn)行記錄統(tǒng)計(jì),比如這樣:
spring.datasource.druid.filters=stat,wall
注意:這里的所有監(jiān)控信息是對(duì)這個(gè)應(yīng)用實(shí)例的數(shù)據(jù)源而言的,而并不是數(shù)據(jù)庫(kù)全局層面的,可以視為應(yīng)用層的監(jiān)控,不可能作為中間件層的監(jiān)控。
本系列教程《Spring Boot 2.x基礎(chǔ)教程》點(diǎn)擊直達(dá)!open in new window。學(xué)習(xí)過(guò)程中如遇困難,建議加入Spring技術(shù)交流群open in new window,參與交流與討論,更好的學(xué)習(xí)與進(jìn)步!
#代碼示例
本文的相關(guān)例子可以查看下面?zhèn)}庫(kù)中的chapter3-3
目錄: