文章目錄
  1. 1. 背景知识
  2. 2. 获取连接的过程

背景知识

c3p0是一个开源的、基于Java JDBC 规范的连接池管理框架。
官网地址:[http://www.mchange.com/projects/c3p0/]

获取连接的过程

  1. c3p0构造了一个Connection对象池。
  2. 在对象池中有空闲对象时或者没有达到对象池最大数量时,获取Connection都会成功返回。但是要注意,获取到的Connection不一定是可用的(比如服务端MySQL进程挂起、或者服务器掉电等极端情况下)。
  3. 由于空闲的Connetion不一定是可用的,所以c3p0会启用一个检查线程池来检查空闲对象的可用性。检查时,会将该空闲对象加入一个idleCheckResources(Set类型);检查完成后,再做出删除操作。其实idleCheckResources就是unused(LinkedList)的一个浅层拷贝。空闲连接检查就是依靠执行select 1 sql来验证。
  4. 在我们获取Connection对象后,会检查获取的Connection是否在idleCheckResources中,如果在则等待mytemplate.pool.timeout时间后,递归调用本方法。
  5. 由于第四步的检查机制,导致一个严重的问题。假设对象池中现在有3个连接,而且都是空闲的。并且这个时候三个检查线程都阻塞在select 1了(比如数据库挂起、或者断电、网络延时),需要说明的是这种阻塞是无限期的,即使在执行statement设置了超时时间也是一直阻塞,何况c3p0源码中并没有设置超时时间。那么这个时候,业务线程已经拿到了其中一个对象,那么进行第4步的检查时,就会导致无限递归方法的出现(这种情况就类似自旋锁的死锁问题了),阻塞住getConnection,最终栈溢出。

下图描述了这一个过程:

那么为什么对于设置了statement的超时时间没有效果呢?这是因为MySQL的statement超时机制决定的,其机制如下图:

关键是第8步,同样是要发送sql来取消执行,所以这种时候取消sql也会被阻塞住。根本原因是阻塞在了socket.read()上了,所以发送的取消sql同样会面临阻塞的问题。statement超时一般是为了控制数据库正常时候sql的超时执行的。对于数据库异常时候的超时,应该使用mysql的socket超时。mysql的socket超时只能在启动参数中配置,没有api来提供配置功能。或者还自己来实现异步的检测机制。

JDBC相关的超时机制详解可以参考:http://www.cubrid.org/blog/dev-platform/understanding-jdbc-internals-and-timeout-configuration/

文章目錄
  1. 1. 背景知识
  2. 2. 获取连接的过程