SpringBoot中好用的數據連接池

前言

日常開發中,數據庫連接池是個必不可少的配置,使用優秀的數據庫連接池,可以有效的提高數據庫訪問效率,降低連接異常等,本篇就來學習一下Spirngboot自帶連接池和阿里Druid兩個最常見的連接池。


什麼是HikariCP

HikariCP是由日本程序員開源的一個數據庫連接池組件,代碼非常輕量,並且速度非常的快。根據官方提供的數據,在i7,開啟32個線程32個連接的情況下,進行隨機數據庫讀寫操作,HikariCP的速度是現在常用的C3P0數據庫連接池的數百倍。在SpringBoot2.0中,官方默認也是使用的HikariCP作為數據庫連接池,可見HikariCP連接池的目的就是為了極致的數據庫連接性能體驗,下面附上一張HikariCP和其他連接池的比較圖:

SpringBoot中好用的數據連接池

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>

這裡的訪問規則為:

  1. deny配置優先於allow,即deny為優先拒絕,即使在allow中配置了白名單,但是隻要存在於deny中,一樣也會被拒絕訪問
  2. 如果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的監控頁面:


SpringBoot中好用的數據連接池


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的配置,通用的配置項如下:

SpringBoot中好用的數據連接池

ruid常見配置與問題

除了我們已經瞭解的druid常見知識以外,開發中經常還會遇到很多其他常見需求,如開啟druid的防sql注入功能、記錄每次執行的sql、數據庫加密、log輸出執行的sql等常見需求,這個時候我們就可以在官方的github的文檔中查找,官方已經給我們整理好了一些開發常見的問題,地址如下:

<code>https://github.com/alibaba/druid/wiki/常見問題/<code>

總結

在實際開發過程中,我們往往會根據自身需求或者項目本身來選擇最適合的連接池,這裡我們將常見的數據連接池從可擴展性、可靠穩定性、性能、可運維性以及自身功能幾個方向進行了比較,可供參考:

SpringBoot中好用的數據連接池


連接池比較圖


分享到:


相關文章: