前言
日常開發中,數據庫連接池是個必不可少的配置,使用優秀的數據庫連接池,可以有效的提高數據庫訪問效率,降低連接異常等,本篇就來學習一下Spirngboot自帶連接池和阿里Druid兩個最常見的連接池。
什麼是HikariCP
HikariCP是由日本程序員開源的一個數據庫連接池組件,代碼非常輕量,並且速度非常的快。根據官方提供的數據,在i7,開啟32個線程32個連接的情況下,進行隨機數據庫讀寫操作,HikariCP的速度是現在常用的C3P0數據庫連接池的數百倍。在SpringBoot2.0中,官方默認也是使用的HikariCP作為數據庫連接池,可見HikariCP連接池的目的就是為了極致的數據庫連接性能體驗,下面附上一張HikariCP和其他連接池的比較圖:

HikariCP連接池性能圖
從圖中的結果可以看出來,HikariCP從性能來說的確一騎絕塵,那麼HikariCP是如何做到這麼極致的性能呢?主要依託於HikariCP自身所做的優化機制。
HikariCP優化機制
字節碼精簡
HikariCP優化了代碼,儘量減少了生成的字節碼,使得cpu可以加載更多程序代碼。
優化了攔截和代理機制
HikariCP對攔截器機制和代理機制進行了代碼優化處理,例如Statement proxy只有100行代碼,大大減少了代碼量,只有其他連接池例如BoneCP的十分之一。
自定義數組
HikariCP針對數組操作進行了自定義數組--FastStatementList,用來替代jdk的ArrayList,優化了get、remove等方法,避免了每次調用get的時候進行範圍檢查,也避免了每次remove操作的時候會將數據從頭到尾進行掃描的性能問題。
自定義集合
同樣的,針對jdk自帶的集合類,HikariCP專門封裝了無鎖的集合類型 ,用來提高在使用中的讀寫併發的效率,減少併發造成的資源競爭問題。
CPU時間片算法優化
HikariCP也對cpu時間片分配算法進行了優化,儘可能使得一個時間片內完成相關的操作。
使用HikariCP
瞭解了HikariCP以後,我們開始使用吧,首先找到HikariCP的座標:
<code> com.zaxxer HikariCP 2.7.6/<code>
然後配置HikariCP對應的配置文件,用來讀取/加載連接池配置:
<code>/** * HikariCP連接池配置 */ @Configuration public class DataSourceConfig { @Value("${spring.datasource.url}") private String dataSourceUrl; @Value("${spring.datasource.username}") private String user; @Value("${spring.datasource.password}") private String password; @Bean public DataSource primaryDataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl(dataSourceUrl); //數據源url config.setUsername(user); //用戶名 config.setPassword(password); //密碼 config.addDataSourceProperty("cachePrepStmts", "true"); //是否自定義配置,為true時下面兩個參數才生效 config.addDataSourceProperty("prepStmtCacheSize", "250"); //連接池大小默認25,官方推薦250-500 config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); //單條語句最大長度默認256,官方推薦2048 config.addDataSourceProperty("useServerPrepStmts", "true"); //新版本MySQL支持服務器端準備,開啟能夠得到顯著性能提升 config.addDataSourceProperty("useLocalSessionState", "true"); config.addDataSourceProperty("useLocalTransactionState", "true"); config.addDataSourceProperty("rewriteBatchedStatements", "true"); config.addDataSourceProperty("cacheResultSetMetadata", "true"); config.addDataSourceProperty("cacheServerConfiguration", "true"); config.addDataSourceProperty("elideSetAutoCommits", "true"); config.addDataSourceProperty("maintainTimeStats", "false"); HikariDataSource ds = new HikariDataSource(config); return ds; } }/<code>
接著我們在SpringBoot的application.properties 文件中進行配置:
<code>server.port=8090 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driverClassName=com.mysql.jdbc.Driver spring.datasource.max-idle=10 spring.datasource.max-active=15 spring.datasource.max-lifetime=86430000 spring.datasource.log-abandoned=truespring.datasource.remove-abandoned=truespring.datasource.remove-abandoned-timeout=60 spring.datasource.initialize=falsespring.datasource.sqlScriptEncoding=UTF-8/<code>
配置完畢,此時我們啟動工程,即可看到控制檯已經將我們配置的HikariCP數據庫連接池信息打印出來了。
阿里巴巴Druid
提到大名鼎鼎的Druid連接池,相信很多人都不陌生,因為該連接池是阿里開源的優秀的連接池,幾乎已經成為現在使用最多的連接池之一。我們先打開Druid的官方github:
<code>https://github.com/alibaba/druid/<code>
可以看到此項目已經有19.4k的star數,並且是2019最受歡迎的開源之一,經歷過真實線上雙十一的考驗,可以說是個很成熟的開源連接池,而Druid連接池專為監控而生,內置強大的監控功能,監控特性不影響性能。功能強大,能防SQL注入,內置Logging能診斷Hack應用行為等。
快速使用
Druid 0.1.18 之後版本都發布到maven中央倉庫中,所以你只需要在項目的pom.xml中加上dependency就可以了,中央倉庫座標如下:
<code> com.alibaba druid ${druid-version}/<code>
這裡我們使用了springBoot,由於默認支持的數據連接池只有四種:dbcp,dbcp2, tomcat, hikariCP,並不包含druid,所以我們這裡也可以選擇直接使用阿里官方編寫的druid-spring-boot-starter,並且我們添加對應的mybatis和pageHelper的依賴:
<code> mysql mysql-connector-java org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.0 com.github.pagehelper pagehelper-spring-boot-starter 1.1.1 com.alibaba druid-spring-boot-starter 1.1.1/<code>
在application.properties中 進行數據源配置:
<code># 數據庫訪問配置# 主數據源,默認的spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/testspring.datasource.username=root spring.datasource.password=123456# 下面為連接池的補充設置,應用到上面所有數據源中# 初始化大小,最小,最大spring.datasource.initialSize=5 spring.datasource.minIdle=5 spring.datasource.maxActive=20# 配置獲取連接等待超時的時間spring.datasource.maxWait=60000# 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒spring.datasource.timeBetweenEvictionRunsMillis=60000# 配置一個連接在池中最小生存的時間,單位是毫秒 spring.datasource.minEvictableIdleTimeMillis=300000 spring.datasource.validationQuery=SELECT 1 FROM DUAL spring.datasource.testWhileIdle=truespring.datasource.testOnBorrow=falsespring.datasource.testOnReturn=false# 打開PSCache,並且指定每個連接上PSCache的大小 spring.datasource.poolPreparedStatements=truespring.datasource.maxPoolPreparedStatementPerConnectionSize=20# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆spring.datasource.filters=stat,wall,log4j# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000# 合併多個DruidDataSource的監控數據#spring.datasource.useGlobalDataSourceStat=true/<code>
配置完畢以後,由於這裡我們使用了mybatis,所以我們還要在application.properties中配置一下mybatis相關:
<code>#mybatis#entity掃描的包名mybatis.type-aliases-package=com.xiaolyuh.domain.model#Mapper.xml所在的位置mybatis.mapper-locations=classpath*:/mybaits/*Mapper.xml#開啟MyBatis的二級緩存mybatis.configuration.cache-enabled=true#pagehelperpagehelper.helperDialect=mysql pagehelper.reasonable=truepagehelper.supportMethodsArguments=truepagehelper.params=count=countSql/<code>
準備就緒後,我們來編寫一個測試類:
<code>@RunWith(SpringRunner.class)@SpringBootTestpublic class DataSourceTests { @Autowired ApplicationContext applicationContext; @Test public voidtestDataSource() throws Exception { // 獲取配置的數據源 DataSource dataSource = applicationContext.getBean(DataSource.class); System.out.println(dataSource.getClass().getName()); } }/<code>
將測試方法運行起來,即可在控制檯中看到對應的數據源的輸出信息。
Druid開啟監控統計功能
druid最強大的功能就是自身提供了對sql的數據監控功能,並且內置了很多詳細的攔截器,可以實現多個角度的攔截處理,那麼如何開啟監控?在Druid中內置提供了一個StatViewServlet用於展示Druid的統計信息,這個StatViewServlet的用途包括:
- 提供監控信息展示的html頁面
- 提供監控信息的JSON API
如果是ssm工程,則可以在web.xml中配置StatViewServlet,如下:
<code> DruidStatView com.alibaba.druid.support.http.StatViewServlet DruidStatView /druid/* /<code>
配置完畢以後,啟動工程則可以按照配置的監控地址訪問監控信息,默認為:
http://ip:port/project-name/druid/index.html
同樣的,在StatViewServlet中我們可以添加訪問密碼的設置,只需要配置Servlet的 loginUsername 和 loginPassword這兩個初始參數 即可,例如:
<code> DruidStatView com.alibaba.druid.support.http.StatViewServlet resetEnable true loginUsername druid loginPassword druid DruidStatView /druid/* /<code>
此時訪問監控頁面的時候就需要輸入我們設置的用戶名和密碼了,如果還想針對用戶有敏感信息配置和訪問權限控制,我們還可以配置allow和deny參數,例如:
<code> DruidStatView com.alibaba.druid.support.http.StatViewServlet allow 128.242.127.1/24,128.242.128.1 deny 128.242.127.4 /<code>
這裡的訪問規則為:
- deny配置優先於allow,即deny為優先拒絕,即使在allow中配置了白名單,但是隻要存在於deny中,一樣也會被拒絕訪問
- 如果allow沒有配置,或者配置為空,默認為全部都可以訪問,不進行白名單限制
使用SpringBoot配置監控
由於我們這裡使用的是SpringBoot,所以我們僅需要在application.properties 添加配置統計攔截的filters:
<code># 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆 spring.datasource.druid.filters=stat,wall,log4j/<code>
這裡的配置是通過別名方式配置擴展支持的插件,如下:
- 監控統計用的filter:stat
- 日誌用的filter:log4j
- 防禦sql注入的filter:wall
接著我們需要在application.properties繼續添加WebStatFilter和StatViewServlet的配置項:
<code># WebStatFilter配置,說明請參考Druid Wiki,配置_配置WebStatFilter#啟動項目後訪問 http://127.0.0.1:8080/druid#是否啟用StatFilter默認值true # WebStatFilter配置,說明請參考Druid Wiki,配置_配置WebStatFilter #啟動項目後訪問 http://127.0.0.1:8080/druid #是否啟用StatFilter默認值true spring.datasource.druid.web-stat-filter.enabled=true spring.datasource.druid.web-stat-filter.url-pattern=/* spring.datasource.druid.web-stat-filter.exclusions=*.js ,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/* #缺省sessionStatMaxCount是1000個 spring.datasource.druid.web-stat-filter.session-stat-max-count=1000 #關閉session統計功能 spring.datasource.druid.web-stat-filter.session-stat-enable=false #配置principalSessionName,使得druid能夠知道當前的session的用戶是誰 #如果你session中保存的是非string類型的對象,需要重載toString方法 spring.datasource.druid.web-stat-filter.principalSessionName=xxx.user #如果user信息保存在cookie中,你可以配置principalCookieName,使得druid知道當前的user是誰 spring.datasource.druid.web-stat-filter.principalCookieName=xxx.user #druid 0.2.7版本開始支持profile,配置profileEnable能夠監控單個url調用的sql列表。 spring.datasource.druid.web-stat-filter.profile-enable=false # StatViewServlet配置,說明請參考Druid Wiki,配置_StatViewServlet配置 #啟動項目後訪問 http://127.0.0.1:8080/druid #是否啟用StatViewServlet默認值true spring.datasource.druid.stat-view-servlet.enabled=true spring.datasource.druid.stat-view-servlet.urlPattern=/druid/* #禁用HTML頁面上的“Reset All”功能 spring.datasource.druid.stat-view-servlet.resetEnable=false #用戶名 spring.datasource.druid.stat-view-servlet.loginUsername=admin #密碼 spring.datasource.druid.stat-view-servlet.loginPassword=admin #IP白名單(沒有配置或者為空,則允許所有訪問) spring.datasource.druid.stat-view-servlet.allow=127.0.0.1,192.168.163.1 #IP黑名單 (存在共同時,deny優先於allow) spring.datasource.druid.stat-view-servlet.deny=192.168.1.73/<code>
接著我們啟動工程,訪問http://localhost/druid ,輸入配置的用戶名:admin以及密碼:admin,即可看到druid的監控頁面:

druid監控頁面
慢sql日誌打印
在開發過程中,往往會遇到sql時間過長問題,為了定位慢sql,我們往往會定義固定時長作為慢sql的時長,而Druid支持慢sql查詢,在Druid中內置提供了一個StatFilter,用於統計監控信息 ,我們可以利用這個StatFilter來統計慢sql:
<code>StatFilter的別名是stat,這個別名映射配置信息保存在druid-xxx.jar!/META-INF/druid-filter.properties/<code>
我們需要在Spring中加入以下配置:
<code> /<code>
當然如果需要我們可以同時開啟多個Filter進行組合使用,中間用,隔開即可,如下:
<code> /<code>
而如果開啟慢sql的記錄,我們需要先定義slowSqlMillis 來配置sql慢查詢的標準,如下:
<code> /<code>
配置完畢以後,所有的超過10s的sql都會在監控頁面的慢sql模塊記錄,可以查看具體的sql以及執行時間等,快速定位開發過程中的慢sql。
DruidDataSource配置
如果我們根據業務的不同,需要更改不同的配置,這個時候我們就需要參考DriudDataSource的配置,通用的配置項如下:
ruid常見配置與問題
除了我們已經瞭解的druid常見知識以外,開發中經常還會遇到很多其他常見需求,如開啟druid的防sql注入功能、記錄每次執行的sql、數據庫加密、log輸出執行的sql等常見需求,這個時候我們就可以在官方的github的文檔中查找,官方已經給我們整理好了一些開發常見的問題,地址如下:
<code>https://github.com/alibaba/druid/wiki/常見問題/<code>
總結
在實際開發過程中,我們往往會根據自身需求或者項目本身來選擇最適合的連接池,這裡我們將常見的數據連接池從可擴展性、可靠穩定性、性能、可運維性以及自身功能幾個方向進行了比較,可供參考:
連接池比較圖