天天看点

C3P0连接池小记

C3P0连接池小记

之前虽然有时候用到数据库连接池,但是一直没有关注过细节,在最近做的一个项目中,遇到了一些问题,这里总结下,不足之处还望指出。

数据库连接池的概念

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。(来自百毒~)

关于需不需要手动释放连接

总的来说,看情况,但是最好自己控制。

之前使用连接池的时候有个误区,以为连接池里的连接拿来不用管,直接每次使用的时候poll.getConnection()就可以了,而没有去释放或者说还给连接池。在一般的小应用中,看不出来问题,但当连接数达到一定的数量时,便会出现问题,比如不需要的连接继续保持着,需要用到连接的进程获取不到连接等等。当然也不排除有些数据库连接池支持通过配置来回收连接,比如配置个最大闲置时间,当时间到了,即使你在程序中没有手动关闭,也会回收当前连接。

但是这样会有个问题。比如配置的连接池最大连接数为30,此时我有60个线程需要使用数据库连接。假设我每个连接只做了查询,可能只需要1s,也就是说,我如果手动关闭,大概每个连接在1s多久可以还给连接池了,这样即使60个线程,也就是有些线程需要等一小会的事情,只要等待超时时间和程序配置合理,也不会有什么大问题。但是如果我没有手动关闭,而配置的自动回收连接的时间又很长,比如说10s(太短了不合适,防止有些连接的操作还没完成就被回收了),那么其实先获取到连接的30个线程在几秒内就完成自己的操作了,而另外的线程需要干等一个个连接被回收,这在很多时候是我们无法忍受的,可能会抛出异常,或者即使不抛出异常,从性能的角度来说也是不合理的。

综上所述,连接最好自己来手动释放,当然为了防止有时候忘了释放,也可以增加闲置时间的相关配置来保证连接最终得到释放。通常而言连接池对连接的释放都是通过代理实现的,不会真的让这个连接die,只是把连接放回连接池里,供需要的线程使用罢了。所以,在你获得了一个连接池的Connection时,用完了之后记得con.close()掉,而且建议放在finally块中,以保证连接被释放。

关于C3P0的连接池问题

1 连接可能并没有真的释放

在我实际的应用中,我需要往mysql中插入一条数据,逻辑本身很简单,但是我是高并发,或者说批量。且不说可以通过一些策略或者程序优化来达到少用连接数的情况,就事论事,我们来看一个现象:

在我的程序中我测试了10000此插入操作,配置的最大连接数100,最小10,每次插入完毕一条记录,我都确定我手动close来连接。但是问题来了:

在程序运行一段时间后,会出现以下异常:

An SQLException was provoked, java.lang.InterruptedException…

An attempt by a client to checkout a Connection has timed out.

理论上来说,我及时的释放了连接,即便每条记录插入的耗时比较久,程序跑的慢了些,也不至于出异常吧?但是事实就是出现了。查阅了很多信息,才发现可能是C3P0的一个所谓的bug:其实你调用了close方法之后,连接可能并没有实时的被关闭,也就是说,可能还是被占用着,那么上面所说的那个问题就会出现了。如果程序不够健壮,那么就会出现这样的异常。

解决方案:

  1. 增大连接数。显然这不治本。
  2. 控制并发量。这个也得看程序本身的需求。
  3. 网上看到有人说增加以下配置即可:调用datasource的setMaxStatments方法,设置C3P0最大缓存的PreparedStatement数量为0,也是就是datasource.setMaxStatements(0);据说可以立即释放连接,但是亲测效果一般。

目前还未找到完美的解决方案,如果读者有,请不吝赐教。

2 连接释放了之后,程序关闭了,连接可能还活着

这个标题有待商榷,源于我发现一个现象:

我使用的是mysql数据库,使用C3P0连接池建立了30个连接,然后把这些连接一一close掉,然后进程结束。然后我去mysql里查当前连接数,发现刚才的30个连接还是存在,只是是sleep状态。

我对mysql不是很熟,也许它sleep一段时间就自己关掉了??假设是这样,还是存在问题。

因为每个数据库都支持的是有限的连接,比如mysql好像默认的就是100,不管是多少,假设是N。我使用了连接池一次占用了n个,然后我可能不断的测试,最后因为每次的n个都没有最终被释放,总有那么个点,使得n的多了之后,达到了N,接着mysql便会报错,“Too many connections”……

当然这种情况一般不会出现,因为你不会一直的启动你的服务再关闭,再启动再关闭,但是搞不好就会碰到这样的问题。

解决方案:

  1. 在mysql中维护,实时监控连接数。
  2. 使用完连接后,最后在程序中加上这句Datasources.destory(datasource);这样就会彻底释放连接。

如有问题,请指教。

继续阅读