- 对于一个简单的数据库应用,因为对于数据库的访问不是很频繁。这时能够简单地在须要访问数据库时,就新建立一个链接,用完后就关闭它,这样作也不会带来什么明显的性能上的开销。可是对于一个复杂的数据库应用,状况就彻底不一样了。频繁的创建、关闭链接,会极大的减低系统的性能,由于对于链接的使用成了系统性能的瓶颈。
- 链接复用。经过创建一个数据库链接池以及一套链接使用管理策略,使得一个数据库链接能够获得高效、安全的复用,避免了数据库链接频繁创建、关闭的开销。
- 对于共享资源,有一个很著名的设计模式:资源池。该模式正是为了解决资源频繁分配、释放所形成的问题的。把该模式应用到数据库链接管理领域,就是创建一个数据库链接池,提供一套高效的链接分配、使用策略,最终目标是实现链接的高效、安全的复用。
数据库链接池的基本原理是在内部对象池中维护必定数量的数据库链接,并对外暴露数据库链接获取和返回方法。如:css
外部使用者可经过getConnection 方法获取链接,使用完毕后再经过releaseConnection 方法将链接返回,注意此时链接并无关闭,而是由链接池管理器回收,并为下一次使用作好准备。html
1. 资源重用因为数据库链接获得重用,避免了频繁建立、释放链接引发的大量性能开销。在减小系统消耗的基础上,另外一方面也增进了系统运行环境的平稳性(减小内存碎片以及数据库临时进程/线程的数量)。java
2. 更快的系统响应速度mysql
数据库链接池在初始化过程当中,每每已经建立了若干数据库链接置于池中备用。此时链接的初始化工做均已完成。对于业务请求处理而言,直接利用现有可用链接,避免了数据库链接初始化和释放过程的时间开销,从而缩减了系统总体响应时间。程序员
3. 新的资源分配手段web
对于多应用共享同一数据库的系统而言,可在应用层经过数据库链接的配置,实现数据库链接池技术,几年钱也许仍是个新鲜话题,对于目前的业务系统而言,若是设计中尚未考虑到链接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库链接数的限制,避免某一应用独占全部数据库资源。spring
4. 统一的链接管理,避免数据库链接泄漏sql
在较为完备的数据库链接池实现中,可根据预先的链接占用超时设定,强制收回被占用链接。从而避免了常规数据库链接操做中可能出现的资源泄漏。数据库
数据库应用,在许多软件系统中常常用到,是开发中大型系统不可缺乏的辅助。但若是对数据库资源没有很好地管理(如:没有及时回收数据库的游标(ResultSet)、Statement、链接 (Connection)等资源),每每会直接致使系统的稳定。这类不稳定因素,不仅仅由数据库或者系统自己一方引发,只有系统正式使用后,随着流量、用户的增长,才会逐步显露。
在基于Java开发的系统中,JDBC是程序员和数据库打交道的主要途径,提供了完备的数据库操做方法接口。但考虑到规范的适用性,JDBC只提供了最直接的数据库操做规范,对数据库资源管理,如:对物理链接的管理及缓冲,指望第三方应用服务器(Application Server)的提供。
本文,以JDBC规范为基础,介绍相关的数据库链接池机制,并就若是以简单的方式,实现有效地管理数据库资源介绍相关实现技术。设计模式
JDBC是一个规范,遵循JDBC接口规范,各个数据库厂家各自实现本身的驱动程序(Driver),以下图所示:
数据库链接池的实现及原理
应用在获取数据库链接时,须要以URL的方式指定是那种类型的Driver,在得到特定的链接后,可按照固定的接口操做不一样类型的数据库,如: 分别获取Statement、执行SQL得到ResultSet等,以下面的例子 :
import java.sql.*;
…
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection dbConn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:oracle","username","password");
Statement st = dbConn.createStatement();
ResultSet rs = st.executeQuery("select * from demo_table");
…some data source operation in here
rs.close();
st.close();
dbConn.close();
在完成数据操做后,还必定要关闭全部涉及到的数据库资源。这虽然对应用程序的逻辑没有任何影响,可是关键的操做。上面是个简单的例子,若是搀和众多的if-else、exception,资源的管理也不免百密一疏。如同C中的内存泄漏问题,Java系统也一样会面临崩溃的恶运。因此数据库资源的管理依赖于应用系统自己,是不安全、不稳定的一种隐患。
在标准JDBC对应用的接口中,并无提供资源的管理方法。因此,缺省的资源管理由应用本身负责。虽然在JDBC规范中,屡次说起资源的关闭/回收及其余的合理运用。但最稳妥的方式,仍是为应用提供有效的管理手段。因此,JDBC为第三方应用服务器(Application Server)提供了一个由数据库厂家实现的管理标准接口:链接缓冲(connection pooling)。引入了链接池( Connection Pool )的概念 ,也就是以缓冲池的机制管理数据库的资源。
JDBC最经常使用的资源有三类:
— Connection: 数据库链接。
— Statement: 会话声明。
— ResultSet: 结果集游标。
分别存在如下的关系 :
数据库链接池的实现及原理
这是一种“爷—父—子”的关系,对Connection的管理,就是对数据库资源的管理。举个例子: 若是想肯定某个数据库链接(Connection)是否超时,则须要肯定其(全部的)子Statement是否超时,一样,须要肯定全部相关的 ResultSet是否超时;在关闭Connection前,须要关闭全部相关的Statement和ResultSet。
所以,链接池(Connection Pool)所起到的做用,不只仅简单地管理Connection,还涉及到 Statement和ResultSet。
ConnectionPool以缓冲池的机制,在必定数量上限范围内,控制管理Connection,Statement和ResultSet。任何数据库的资源是有限的,若是被耗尽,则没法得到更多的数据服务。
在大多数状况下,资源的耗尽不是因为应用的正常负载太高,而是程序缘由。
在实际工做中,数据资源每每是瓶颈资源,不一样的应用都会访问同一数据源。其中某个应用耗尽了数据库资源后,意味其余的应用也没法正常运行。所以,ConnectionPool的第一个任务是限制:每一个应用或系统能够拥有的最大资源。也就是肯定链接池的大小(PoolSize)。
ConnectionPool的第二个任务:在链接池的大小(PoolSize)范围内,最大限度地使用资源,缩短数据库访问的使用周期。许多数据库中,链接(Connection)并非资源的最小单元,控制Statement资源比Connection更重要。以Oracle为例:
每申请一个链接(Connection)会在物理网络(如 TCP/IP网络)上创建一个用于通信的链接,在此链接上还能够申请必定数量的Statement。同一链接可提供的活跃Statement数量能够达到几百。在节约网络资源的同时,缩短了每次会话周期(物理链接的创建是个费时的操做)。但在通常的应用中,多数按照2.1范例操做,这样有10个程序调用,则会产生10次物理链接,每一个Statement单独占用一个物理链接,这是极大的资源浪费。 ConnectionPool能够解决这个问题,让几10、几百个Statement只占用同一个物理链接, 发挥数据库原有的优势。
经过ConnectionPool对资源的有效管理,应用能够得到的Statement总数到达 :
(并发物理链接数)×(每一个链接可提供的Statement数量)
例如某种数据库可同时创建的物理链接数为 200个,每一个链接可同时提供250个Statement,那么ConnectionPool最终为应用提供的并发Statement总数为: 200 × 250 = 50,000个。这是个并发数字,不多有系统会突破这个量级。因此在本节的开始,指出资源的耗尽与应用程序直接管理有关。
对资源的优化管理,很大程度上依靠数据库自身的JDBC Driver是否具有。有些数据库的JDBC Driver并不支持Connection与Statement之间的逻辑链接功能,如SQLServer,咱们只能等待她自身的更新版本了。
对资源的申请、释放、回收、共享和同步,这些管理是复杂精密的。因此,ConnectionPool另外一个功能就是,封装这些操做,为应用提供简单的,甚至是不改变应用风格的调用接口。
根据第二章中原理机制,Snap-ConnectionPool(一种简单快速的链接池工具,可在www.snapbug.net下载)按照部分的JDBC规范,实现了链接池所具有的对数据库资源有效管理功能。
在JDBC规范中,应用经过驱动接口(Driver Interface)直接方法数据库的资源。为了有效、合理地管理资源,在应用与JDBC Driver之间,增长了链接池: Snap-ConnectionPool。而且经过面向对象的机制,使链接池的大部分操做是透明的。参见下图,Snap-ConnectionPool的体系:
数据库链接池的实现及原理
图中所示,经过实现JDBC的部分资源对象接口( Connection, Statement, ResultSet ),在 Snap-ConnectionPool内部分别产生三种逻辑资源对象: PooledConnection, PooledStatement和 PooledResultSet。它们也是链接池主要的管理操做对象,而且继承了JDBC中相应的从属关系。这样的体系有如下几个特色:
— 透明性。在不改变应用原有的使用JDBC驱动接口的前提下,提供资源管理的服务。应用系统,如同原有的 JDBC,使用链接池提供的逻辑对象资源。简化了应用程序的链接池改造。
— 资源封装。复杂的资源管理被封装在 Snap-ConnectionPool内部,不须要应用系统过多的干涉。管理操做的可靠性、安全性由链接池保证。应用的干涉(如:主动关闭资源),只起到优化系统性能的做用,遗漏操做不会带来负面影响。
— 资源合理应用。按照JDBC中资源的从属关系,Snap-ConnectionPool不只对Connection进行缓冲处理,对Statement也有相应的机制处理。在2.3已描述,合理运用Connection和Statement之间的关系,能够更大限度地使用资源。因此,Snap- ConnectionPool封装了Connection资源,经过内部管理PooledConnection,为应用系统提供更多的Statement 资源。
— 资源连锁管理。Snap-ConnectionPool包含的三种逻辑对象,继承了JDBC中相应对象之间的从属关系。在内部管理中,也依照从属关系进行连锁管理。例如:判断一个Connection是否超时,须要根据所包含的Statement是否活跃;判断Statement也要根据 ResultSet的活跃程度。
ConnectionPool是Snap-ConnectionPool的链接池对象。在Snap-ConnectionPool内部,能够指定多个不一样的链接池(ConnectionPool)为应用服务。ConnectionManager管理全部的链接池,每一个链接池以不一样的名称区别。经过配置文件适应不一样的数据库种类。以下图所示:
数据库链接池的实现及原理
经过ConnectionManager,能够同时管理多个不一样的链接池,提供通一的管理界面。在应用系统中经过 ConnectionManager和相关的配置文件,能够将凌乱散落在各自应用程序中的数据库配置信息(包括:数据库名、用户、密码等信息),集中在一个文件中。便于系统的维护工做。
对2.1的标准JDBC的使用范例,改成使用链接池,结果以下:
import java.sql.*; import net.snapbug.util.dbtool.*; //… ConnectionPool dbConn = ConnectionManager .getConnectionPool("testOracle" ); Statement st = dbConn.createStatement(); ResultSet rs = st.executeQuery( "select * from demo_table" ); //… some data source operation in herers.close();st.close();
在例子中,Snap-ConnectionPool封装了应用对Connection的管理。只要改变JDBC获取Connection的方法,为获取链接池(ConnectionPool)(粗体部分),其余的数据操做均可以不作修改。按照这样的方式,Snap- ConnectionPool可帮助应用有效地管理数据库资源。若是应用忽视了最后资源的释放: rs.close() 和 st.close(),链接池会经过超时(time-out)机制,自动回收。
不管是Snap-ConnectionPool仍是其余的数据库链接池,都应当具有一下基本功能:
-对源数据库资源的保护
-充分利用发挥数据库的有效资源
-简化应用的数据库接口,封闭资源管理。
-对应用遗留资源的自动回收和整理,提升资源的再次利用率。
在这个前提下,应用程序才能投入更多的精力于各自的业务逻辑中。数据库资源也再也不成为系统的瓶颈。
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>
spring: application: name: springboot-test-exam1 datasource: # 使用阿里的Druid链接池 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver # 填写你数据库的url、登陆名、密码和数据库名 url: jdbc:mysql://localhost:3306/databaseName?useSSL=false&characterEncoding=utf8 username: root password: root druid: # 链接池的配置信息 # 初始化大小,最小,最大 initial-size: 5 min-idle: 5 maxActive: 20 # 配置获取链接等待超时的时间 maxWait: 60000 # 配置间隔多久才进行一次检测,检测须要关闭的空闲链接,单位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一个链接在池中最小生存的时间,单位是毫秒 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false # 打开PSCache,而且指定每一个链接上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 # 配置监控统计拦截的filters,去掉后监控界面sql没法统计,'wall'用于防火墙 filters: stat,wall,log4j # 经过connectProperties属性来打开mergeSql功能;慢SQL记录 connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 # 配置DruidStatFilter web-stat-filter: enabled: true url-pattern: "/*" exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*" # 配置DruidStatViewServlet stat-view-servlet: url-pattern: "/druid/*" # IP白名单(没有配置或者为空,则容许全部访问) allow: 127.0.0.1,192.168.163.1 # IP黑名单 (存在共同时,deny优先于allow) deny: 192.168.1.73 # 禁用HTML页面上的“Reset All”功能 reset-enable: false # 登陆名 login-username: admin # 登陆密码 login-password: 123456
为了方便使用application.properties的读者,使用下面的配置和上面相同
server.port=8080 spring.application.name=springboot-test-exam1 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/databaseName?useSSL=false&characterEncoding=utf8 spring.datasource.username=root spring.datasource.password=root spring.datasource.druid.initial-size=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.maxActive=20 spring.datasource.druid.maxWait=60000 spring.datasource.druid.timeBetweenEvictionRunsMillis=60000 spring.datasource.druid.minEvictableIdleTimeMillis=300000 spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL spring.datasource.druid.testWhileIdle=true spring.datasource.druid.testOnBorrow=false spring.datasource.druid.testOnReturn=false spring.datasource.druid.poolPreparedStatements=true spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.druid.filters=stat,wall,log4j spring.datasource.druid.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 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/* spring.datasource.druid.stat-view-servlet.url-pattern=/druid/* spring.datasource.druid.stat-view-servlet.allow=127.0.0.1,192.168.163.1 spring.datasource.druid.stat-view-servlet.deny=192.168.1.73 spring.datasource.druid.stat-view-servlet.reset-enable=false spring.datasource.druid.stat-view-servlet.login-username=admin spring.datasource.druid.stat-view-servlet.login-password=123456
运行结果
访问:http://localhost:8080/druid/,登陆名:admin,密码123456
https://blog.csdn.net/weixin_...
https://www.cnblogs.com/wym78...