本文概览:主要介绍了如下部分,(1)介绍了数据库连接池和Druid功能。(2)Druid相关操作,如下:

  • 连接池初始化
  • 从连接池获取Connection
  • 通过connection执行sql
  • 关闭connection

1 数据连接池介绍

1.1 数据库连接池引入

1、背景

通过JDBC直接访问数据库,使用这种方式,每一次执行sql,都需要创建新的连接,执行sql完成之后,还需要关闭连接。如下:

JDBC实例之mysql

当使用JDBC创建数据库连接时候,需要耗费很大的资源。如果在程序中,每次需要访问数据库时候,都进行数据库连接,那么势必会造成性能低下;同时,如果用户失误忘记释放数据库连接,会导致资源的浪费等。而数据库连接池就是解决该问题,通过管理连接池中的多个连接对象(connection),实现connection重复利用。从而,大大提高了数据库连接方面的性能。

2、连接池现状

目前存在多个开源的java数据库连接池,如DBCPC3P0Druid等,这些连接池都是基于JDK的线程池接口来实现,接口如下

连接池中常涉及到两个参数有:

  • 最小连接数:初始化时,创建该数目的connection放入连接池中。
  • 最大连接数:允许创建connection的最大数值。当系统请求连接时候,且连接池中不存在空闲的连接,如果connection总数未超过最大连接数,那么连接池负责创建新的connection对象,并返回该对象;如果connection总数已经到达该最大连接数,那么连接池将用户请求转入等待队列。

1.2 Druid连接池介绍

Druid首先实现了一个数据库连接池的功能,这个功能和其他连接池没有什么差异,都是维护一个连接集合,包括数据库连接的创建、关闭等。但是Druid相比其他连接池,提供了监控功能,就通过filter-chain来实现各个filter的操作,实现对sql的监控,这正是Druid的价值所在。

Druid除了提供了一个数据库连接池功能。还具有如下功能:

1)可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。

2)数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiverDruidDataSource都支持PasswordCallback

3SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-LoggingLog4jJdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。

(4)自定义过滤器。扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter机制,很方便编写JDBC层的扩展插件。

1.3 Druid与ConnectionPoolDataSource比较

实现了jvax.sql.ConnectionPoolDataSource的接口中第一个函数接口,对于第二个函数不进行支持

2 连接池存储结构

连接池中的连接存储:Conetions和activeConnections两个数组。

有以下说明:

  • 连接池中保存的元素是什么?是DruidConnectionHolder(保存了JDK的connection对象)。
  • 连接池中每一个连接,用完之后必须调用close()。在close中通过recyCle()把连接从activeConections集合中重新移动到connections[],达到重复利用连接的目的

1、关于DruidDataSource的activeConnections

  • 在activeConections中添加连接,是在DruidDataSource#getConnection时进行获取的。
  • 删除activeConnections中连接,在关闭连接DruidPooledConneciton#close中使用DruidDataSource#recycle,如下:

所以,使用DruidDataSouce连接池获取数据库连接时,在使用完这个connection之后,如果没有进行close操作,那么activeConnections就不会删除,只会增加。此时因为线程池中动态创建连接的线程CreateConnectionThread中判断一个条件就是

此时一个场景就是poolingCount=0,但是activeCount的线程数目是已经达到了maxAcive,那么此时线程池就会发现无法再提供连接的故障。

为了解决这个问题,所以在init函数中启动了一个DestroyConnectionThread线程来完成这件事情。但是需要用户设置一个removeAbandoned变量才可以生效

2、connections

(1) connections添加 一个连接      

  • 在init()初始化时进行增加;
  • 在createAndStartCreatorThread中进行增加

(2)connections删除一个连接       

在getConnectionInternal中获取连接时,进行删除。通过这个函数获取的连接已经被关闭,那么此时在调用这个函数的getConnectionDirect中会进行判断这个获取连接是否关闭,如果关闭,再次通过getConnectionInternal进行获取,如下代码:

3 连接池初始化

通过init函数进行初始化连接池。这个函数在getConection()时进行调用。在执行init函数时,首先会初始化存放连接的数组conections,然后启动两个线程

(1)CreateConnectionThread,动态在connections中添加连接

(2)DestroyConnectionThread,动态删除actiConnections中很久不用的连接。

3.1 通过init来初始化

1、通过init函数完成初始化

2、初始化时机

(1)方式1:在配置文件中

(2)方式2:通过第一次执行getConnection

3.2 动态创建连接CreateConnectionThread

该线程负责动态的创建连接,维护连接数在initSize到maxActive之间。如下代码说明了在连接池动态创建一个连接的时机:

3.3  动态删除连接DestroyConnectionThread

该线程的作用就是:关闭 连接池connnections[]空闲连接    连接池activeConections[]中超时运行连接 。

1、创建并启动线程代码如下:

2、通过DetroyTask来实现,包括两部分:

  • 关闭 连接池connnections[]中 空闲时间超过一个阈值的连接
  • 关闭  连接池activeConneection[] 运行超过一个阈值的连接

(1)shrink(true)

用于关闭connecitons[]中空闲连接数,涉及到属性有:

  • minIdle:最小空闲连接数
  • minEvictableIdleTimeMillis 空闲时间的阈值,超过改阈值需要进行关闭该空闲连接

(2)removeAbandoned()

处理activeConenctions[]中运行超时连接,涉及到属性有:

  • removeAbandoned。是否需要处理activeConections超时连接:true表示处理;false表示不处理。
  • removeAbandonedTimeoutMillis。运行超时的阈值

4 从连接池获取Connection

通过两种方式可以获取连接

  • 直接获取
  • 通过filter-chain获取

4.1 情况1 使用filter-chain模式来产生新的连接

查看FilterChainImpl#dataSource_connect实现代码

这里使用filter的目的,就在于在filter可以做一些操作,如下两个例子

  • 以LogFilter#dataSource_getConnection为例

  • 以StatFilter#dataSource_getConnection为例

由上,filter操作模板就是包括3步:

所以,假设DruidDataSources中filters包括三个filter:filter1,filter2,filter3,每一个dataSource_getConnection代码模板为:

则在DruidDataSource执行getConnection()之后,此时打印结果就是:(类似于递归,先走到底,然后再返回依次执行)

4.2 情况2 直接获取getConnectionDirect()

在getConnectionDirect中获取流程

1、getConnectionInternal(maxWaitMillis);

在DruidDataSource中的如下代码:

(1)lock

这也可以解释为什么在获取连接时,需要增加超时时间,因为lock是阻塞的。 在获取连接时,如果设置了超时,则获取连接时间如果超时则会抛出异常(参考pollLast(nanos)中的代码)

(2) pollLast(nanos)

(3)takeLast

2、testConnectionInternal(poolableConnection.getConnection())

通过使用获取的conneciton执行一个测试sql来校验是否生效。

3、isTestOnBorrow()和isTestWhileIdle()比较

二者只能有一个为true,如果都为true,优先选择isTestOnBorrow()。

(1)共同点

都是校验connection可用

(2)区别

isTestWhileIdle()只针对空闲时间超过一定时间的连接。而testConnectionInternal是针对所有连接

4、涉及到属性有:

  • maxWait,获取连接时的最大等待时间
  • testOnBorrow,是否校验connection有效可用。
  • validationQuery,在testOnBorrow为true情况下,获取连接后,通过执行该sql校验connection是否正常
  • validationQueryTimeout,在testOnBorrow为ture情况下,执行validationQeury时,超时时间,默认不进行超时设置
  • testWhileIdle,超过一个空闲等待时间的连接,进行测试conneciton是否有效可用。在testOnBorrow为false情况下使用。
  • timeBetweenEvictionRunsMillis,空闲等待时间,testWhileIdle=true的时候使用

5 通过Connection执行Sql工作机制

druid最大价值就在于提供监控功能。通过filter-chain来实现各个filter的操作(如自定义了一个FilterEventAdapter,那么就可以实现对执行sql的监控)。

使用filter执行sql的流程:

(1)第一步 初始化连接池

(2)第二步  获取DruidPooledPreparedStatement,这个类中的statement的值有两种情况。

(3)第三步 执行PreparedStatementProxyImpl

5.1 介绍

在“从连接池获取连接”小节 中实现ConnectionPoolDataSource#getPooledConnection()查询连接时,使用filter-chain模式来产生新的连接,类似于递归。filter-chain除了上述获取连接,还有查询、更新等数据库操作。以执行查询为例,查看failterChainimpl的statement_executeQuery操作为例,如下代码:

5.2 实现原理

通过druid连接池得到DruidPooledConnection,通过这个连接获取到DruidPooledStatement.

1、初始化连接

在初始化连接池时,需要创建连接,分为如下两种情况:

  • 如果filters不为空,则在连接池中中DruidConnectionHolder包含的connectio的类型就是ConnectionProxyImpl。
  • 如果filters为空,则在连接池中DruidConnectionHolder包含的connectio的类型就是Connection。

2、获取DruidPooledPreparedStatement

(1)从初始化连接池中获取连接DruidPooledConnection,

(2)通过DruidPooledConnection来获取DruidPooledPreparedStatement。这里分为两种情况:

  • 包含过滤器(即DruidDataSource中filters不为空)
  • 不包含过滤器(即DruidDataSource中filters为空)

查看DruidPooledConnection#prepareStatement操作

3、执行PreparedStatementProxyImpl实现监控功能

如果包含过滤器就通过PreparedStatementProxyImpl来执行sql,查看执行函数

4、 如何确认是否包含过虑器?

就是看DruidDataSource中filters是否为空,即看用户在配置DruidDataSource时,是否配置了filters。

6 关闭连接

通过关闭连接实现回收连接,达到可重复利用connection的目的。在执行close操作时也分为:使用过滤器和不使用过滤器两种,如下代码

在执行DruidPooledConnections#close,执行如下recycle,分为是否回收连接两种情况:

1、设置DruidPooledConnections的abandoned属性为true的时机

在DestroyConnectionThread线程执行DestroyTask时,在通过removeAbandoned()关闭超时运行连接时,设置了abndoned属性为true:

removeAbandoned()执行逻辑如下,通过JDBC关闭超时连接,并设置连接的abandoned属性为true

2、dataSource.recycle(this)

作用:将conneciton重新放到connections[]中。

3、涉及到相关属性有

  • testOnReturn  是否需要对回收的连接,检查有效可用

(全文完)

分类&标签