数据源库连接池
为什么
数据库连接是一种关键、有限且昂贵的资源,创建和释放数据库连接是一个很耗时的操作,频繁地进行这样的操作将占用大量的性能开销,进而导致网站的响应速度下降,严重的时候可能导致服务器崩溃;数据库连接池可以节省系统许多开销。
是什么
数据库连接池(Database Connection Pooling)在程序初始化时创建一定数量的数据库连接对象并将其保存在一块内存区中,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接以避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
数据库连接池不仅负责创建数据库连接,还负责分配、管理和释放数据库连接,可以通过配置连接池的参数来控制连接池中的初始连接数、最小连接、最大连接、最大空闲时间等。
最小连接数
是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
最大连接数
限定了连接池能存储的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会造成应用程序因不能立刻获得数据库连接而对程序产生功能性影响。
最小连接数与最大连接数差距
最小连接数与最大连接数相差太大,最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放
原理
程序初始化时创建一定数据量的数据库连接对象存储在内存中,当程序需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象;如果连接池内没有空闲连接,连接池创建新的数据库连接对象并将这个连接对象提供给应用程序使用,应用程序用完后,数据库连接池不会立刻关闭该连接,在空闲时间超过最大空闲时间时,该连接才被关闭;当程序需要再次访问数据库时,数据库连接池会将空闲时间最长的那个连接提供给应用程序; 如果数据库连接请求超过最大连接数,则该数据库连接请求被加入到等待队列中;程序退出时,数据库连接池断开所有连接并释放资源。
常用连接池
1、DBCP:DBCP(DataBase connection pool)数据库连接池是apache上的一个 java连接池项目,也是 tomcat使用的连接池组件;单独使用dbcp需要3个包:common-dbcp.jar,common-pool.jar,common-collections.jar.dbcp没有自动回收空闲连接的功能。官网:http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi
2、C3P0:C3P0是一个开源的jdbc连接池,它实现了数据源和jndi绑定,支持jdbc3规范和jdbc2的标准扩展。c3p0是异步操作的,缓慢的jdbc操作通过帮助进程完成。扩展这些操作可以有效的提升性能。目前使用它的开源项目有Hibernate,Spring等。c3p0有自动回收空闲连接功能。注: JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口。官网:https://sourceforge.net/projects/c3p0/
3、Druid:
Druid数据库连接池是阿里巴巴开源平台上的一个开源项目,该连接池性能高效,简单SQL语句用时10微秒以内,复杂SQL用时30微秒。官网:https://github.com/alibaba/druid
4、HikariCP:
HikariCP数据库连接池尽管是后起之秀,但却PK掉其它数据库连接池技术,成为目前速度最快的数据库连接池,SpringBoot2.0也已经采用HikariCP作为默认连接池配置。官网:http://brettwooldridge.github.io/HikariCP/
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" lazy-init="false" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> <!-- 连接只读数据库时配置为true, 保证安全 --> <property name="readOnly" value="false" /> <!-- 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒 --> <property name="connectionTimeout" value="30000" /> <!-- 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟 --> <property name="idleTimeout" value="600000" /> <!-- 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQL wait_timeout参数(show variables like '%timeout%';) --> <property name="maxLifetime" value="1800000" /> <!-- 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count) --> <property name="maximumPoolSize" value="15" /> </bean> ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml"); System.out.println(application.getBean("dataSource")); application.close(); |
Spring配置连接池
为了便于系统的维护和管理,常常将文件路径或数据源配置等信息定义在properties文件中,但这些信息有时需要在配置Spring xml文件时读取到,为此Spring提供了一个PropertyPlaceholderConfigurer类,在Spring配置文件中配置PropertyPlaceholderConfigurer并为其location变量指定properties文件路径,然后即可使用${}的形式将properties文件中的配置信息读取出来,例子如下:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:config/jdbc.properties"></property> </bean> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" lazy-init="false" destroy-method="close"> <property name="driverClassName" value="${driverClassName}"></property> <property name="jdbcUrl" value="${jdbcUrl}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> <property name="readOnly" value="${readOnly}" /> <property name="connectionTimeout" value="${connectionTimeout}" /> <property name="idleTimeout" value="${idleTimeout}" /> <property name="maxLifetime" value="${maxLifetime}" /> <property name="maximumPoolSize" value="${maximumPoolSize}" /> </bean> ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml"); System.out.println(application.getBean("dataSource")); application.close(); |
在Spring 2.5之后,Spring提供了一个context命名空间,通过context:property-placeholder可以简化上面灰色背景内的代码:
<context:property-placeholder location="classpath:config/jdbc.properties"/> |
上面代码红色代码部分如下:
在src目录中创建一个名为config包,在该包中创建一个名为jdbc.properties的文件,将如下内容定义在该文件中: driverClassName=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8 username=root password=root readOnly=false connectionTimeout=30000 idleTimeout=600000 maxLifetime=1800000 maximumPoolSize=15 |
数据库连接池性能比对
数据库连接池实现方式很多,下面以 MySQL数据库为测试环境,比对各数据库连接池的性能
|
功能
说明:
1、由于boneCP被hikariCP替代,并且已经不再更新,boneCP没有进行调研。
2、proxool网上有评测说在并发较高的情况下会出错,proxool便没有进行调研。
3、druid的功能比较全面,且扩展性较好,比较方便对jdbc接口进行监控跟踪等。
4、c3p0历史悠久,代码极其复杂,不利于维护,且存在deadlock的潜在风险。
性能
|
环境配置:
获取关闭连接性能测试
测试说明:
1、初始连接和最小连接均为5,最大连接为20。在borrow和return均不心跳检测;
2、其中打开关闭次数为: 100w次;
3、测试用例和mysql在同一台机器上面,尽量避免io的影响;
4、使用mock和连接mysql在不同线程并发下的响应时间
|
图形:
|
mysql性能数据 (单位:ms)
|
mock性能数据 (单位:ms)
|
结果:
1、mock和mysql连接性能表现差不多,主要是由于初始化的时候建立了连接后期不再建立连接,和使用mock连接逻辑一致。
2、性能表现:hikariCP>druid>tomcat-jdbc>dbcp>c3p0。
3、hikariCP 的性能及其优异。hikariCP号称java平台最快的数据库连接池。
4、hikariCP在并发较高的情况下,性能基本上没有下降。
5、c3p0连接池的性能很差,不建议使用该数据库连接池。
hikariCP性能分析:
1、hikariCP通过优化(concurrentBag,fastStatementList )集合来提高并发的读写效率。
2、hikariCP使用threadlocal缓存连接及大量使用CAS的机制,最大限度的避免lock,但可能带来cpu使用率的上升。
3、从字节码的维度优化代码。 (default inline threshold for a JVM running the server Hotspot compiler is 35 bytecodes )让方法尽量在35个字节码一下,来提升jvm的处理效率。
查询一条语句性能测试
测试说明:
1、初始连接和最小连接均为8,最大连接为8。在borrow和return均不心跳检测
2、查询的次数为10w次,查询的语句为 1:打开连接 2:执行 :select 1 3:关闭连接
3、测试用例和mysql在同一台机器上面,尽量避免io的影响
|
图形:
|
测试数据:
1、在并发比较少的情况下,每个连接池的响应时间差不多。是由于并发少,基本上没有资源竞争。
2、在并发较高的情况下,随着并发的升高,hikariCP响应时间基本上没有变动。
3、c3p0随着并发的提高,性能急剧下降。
pscache性能对比
说明:
1、通过druid进行设置pscache和不设置pscache的性能对比
2、初始连接和最小连接均为8,最大连接为8。在borrow和return均不心跳检测。并且执行的并发数为8.
3、查询10w次。查询流程为:1:建立连接,2:循环查询preparestatement语句 3:close连接
4、测试用例和mysql在同一台机器上面,尽量避免io的影响
|
测试数据:
测试结果:
开启psCache缓存,性能大概有20%幅度的提升。可考虑开启pscache.
测试说明:
1、psCache是connection私有的,所以不存在线程竞争的问题,开启pscache不会存在竞争的性能损耗。
2、psCache的key为prepare执行的sql和catalog等,value对应的为prepareStatement对象。开启缓存主要是减少了解析sql的开销。
结论
1、性能方面 hikariCP>druid>tomcat-jdbc>dbcp>c3p0 。hikariCP的高性能得益于最大限度的避免锁竞争。
2、druid功能最为全面,sql拦截等功能,统计数据较为全面,具有良好的扩展性。
3、综合性能,扩展性等方面,可考虑使用druid或者hikariCP连接池。
4、可开启prepareStatement缓存,对性能会有大概20%的提升。
特此声明:本篇博客所有内容均转自我的前辈所给的Word文档,仅用于学习与后期记忆!