SpringBoot中好用的数据连接池

前言

日常开发中,数据库连接池是个必不可少的配置,使用优秀的数据库连接池,可以有效的提高数据库访问效率,降低连接异常等,本篇就来学习一下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>

总结

在实际开发过程中,我们往往会根据自身需求或者项目本身来选择最适合的连接池,这里我们将常见的数据连接池从可扩展性、可靠稳定性、性能、可运维性以及自身功能几个方向进行了比较,可供参考:


连接池比较图