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中好用的数据连接池


连接池比较图


分享到:


相關文章: