关于connection pool的讨论

ajoo 2003-11-05 12:57:39
连接池是个很cool的东西,很多系统,open source的也好,商用的也好,自己开发的也好,好像都有这个东西。


前几天,我们买的一个系统发生了宕机的情况。研究后发现,是因为这个系统用的数据库重新启动过了。而这个系统使用的是connection pool,一旦数据库系统重启,连接池内缓冲的连接都失效了。但是,问题是,这个连接池没有考虑到这个问题,于是整个系统都无法正常工作了。

前段时间,我们在一个项目中使用一个open source的o/r mapping软件ojb。它自带一个连接池。但是,它也有类似的问题,一旦数据库重新启动,或者数据库cluster fail over了,这个application就无法工作了。

我仔细研究了源代码,发现,要想扩展代码来对付这个问题几乎不可能。这个软件设计的时候没有考虑这个问题,而且,它的结构设计的不够灵活,无法根据customize。最后,只好费尽了九牛二虎之力,disable了这个connection pool。


这些失败的教训,让我对象连接池这种复杂的功能非常谨慎。不知道大家平时在使用连接池或者实现连接池的时候,是如何对付这种问题的?
...全文
198 20 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
truezerg 2003-11-06
  • 打赏
  • 举报
回复
那看来只能在取连接的时候判断了。 那样的话,连接池的效率是有点慢了。不过如果你能忍受的话也无所谓。

ajoo 2003-11-06
  • 打赏
  • 举报
回复
eyeieye(魔之眼):
谢谢。我下载了c3p0,看了一下它的代码。似乎,它也是在给用户返回一个connection的时候(当然,它也有idle时候的检查)检查这个连接的状态,通过的方法就是检查一个预先创建好的表是否还在。
也一样是一个roundtrip的检查了(看来这必不可少)

它还通过检查SQLException里面的error code,它认为state code 08001, 08001代表数据库无效,需要重新启动连接池。


truezerg,看来不浪费时间来保证连接有效是不可能的了。只能说,做这个测试的时间,远远小于创建连接本身的时间。
另外,连接的有效性必须在连接被返回给用户之前做,返回给用户后,再试图处理恢复就很复杂了。好在,database fail over这种东西,可以允许某些用户的连接失败,只要新用户的连接能够成功,就好了。

truezerg 2003-11-05
  • 打赏
  • 举报
回复
我随便想到的,瞎说不知道行不行。

你们不是有源码吗? 别的地方都不改,只改一下从池中取连接的地方,在从池中取连接的时候首先判断该连接是否有效。如果没有效就在计数器上加一,同时关闭掉该连接并且不允许cache,如果计数器累加值超过5(这个数由你定,你认为有多少个连接无效可视为数据库重启了,就定到几),就意味着数据库重启了。这样你就清池。

这个方法确实有点浪费时间,因为毕竟重启的可能性少,但却必需在取连接时都要检查。写到这里看来这个方法是不行了。

这个问题最关键的地方是如何知道在池中取得的连接是否有效。这个问题解决了,就可以在一定的条件下(比如我取了五次链接都是无效的)来判断数据库是否重启了。 判断出是否重启了,就可以清池然后就解决了。

要找到一个即不怎么浪费时间的做法,又要得到结果。 看来挺难

又突然想到一个办法。 ajoo你不是说可能会出现很多种情况导致出现SQLException异常吗? 你可不可以这样,在出现SQLException的时候判断出现异常的connection是否有效,如果有效就说明是其它的情况出现的,如果无效就计数器加一,加到一定时候就清池。 出现SQLException的机会比每次出池时都检查相对少用了很多时间。把检查连接是否有效放在这里好像比较好一点。 (不过我不知道出现一个异常以后连接会不会自动关掉,如果是自动关掉的话,放在异常里检查就不行了。 ajoo 你试一下吧)

ajoo你想到什么好点子没有?
truezerg 2003-11-05
  • 打赏
  • 举报
回复
ajoo, 想一想,应用服务器重启都做了哪些事情? 有点启发吗? 我现在没太想好这个问题。再想一想。

如果解决了“如何知道数据库重启了”,那就清一下池。这样能行吗?
eyeieye 2003-11-05
  • 打赏
  • 举报
回复
dbcp我们也尝试过,websphere自带的数据源也试过,似乎都不好用(测试语句也太费了一点,必须定时执行)
icecloud 2003-11-05
  • 打赏
  • 举报
回复
这个问题困扰了我好久
最后采用了jakarta的commons-DBCP才得以解决

他们的解决方法是,定一个Query语句作为测试用
一旦数据库发生错误,就测试Connection是否连通
如果连不通则抛弃重新建立Conn

很简单的:〉
eyeieye 2003-11-05
  • 打赏
  • 举报
回复
我们也使用o/r mapping 只不过我们使用的是hibernate,他本身自带的就是c3p0
eyeieye 2003-11-05
  • 打赏
  • 举报
回复
我们也遇到了这个问题,是因为客户的数据库服务器定时会关毕一会。
解决办法竟然这么简单,使用c3p0,一个开源的pool,自带连接恢复功能。
ajoo 2003-11-05
  • 打赏
  • 举报
回复
truezerg,我们的系统目前没有好的办法,一旦数据库重新启动或者fail over,必须手工重新启动应用服务器。
这个系统本来目标是24/7,看来做不到了。
ajoo 2003-11-05
  • 打赏
  • 举报
回复
有时候,SQLException可能就是普通的异常,比如,试图插入重复key;网络暂时无法连接;资源timeout;资源死锁;硬盘空间耗尽;sql语法错;sql语句内部类型匹配错(比如,试图比较整数和字符串);jdbc api的类型匹配错, 等等等等。


有些错误可以认为无法恢复(如网络失败或者硬盘耗尽),但是有些错,比如类型匹配错,资源申请暂时超时等应该可以恢复的。


cbhyk() ,你的代码里面的“用conn执行一下数据库操作”就是随便执行一下测试语句?
这样,不是就引入了一个来回roundtrip?感觉很不舒服,而且也有违与连接池的高效率的目的。

而且,如果遇到fail over的情况,不希望简简单单地抱错,最理想的情况是,连接池可以发现问题,自动关闭放弃当前的连接,并且重新产生新的连接。所有这一切应该透明于用户,甚至最好对应用程序透明。

据我想,一个可能的方案是,连接池内部纪录一个当前的池版本id,并且把这个id赋给每一个生成的Connection对象。
一旦发生SQLException,连接池就进入一个特殊的“清理”状态。这个状态内部,把当前的版本id加一,把正在cache的连接全部关掉。遇到连接返回池的时候,检查版本id,如果不同于新的版本id,就关掉它而不是缓存。


但是,这个方法的一个问题是:如果万一某一个连接发生了任何一个上述的SQLException,那么就意味着整个池进入这个“清理”状态。
而连接池必须封装所有的ResultSet, Statement,Connection等 jdbc对象,一个对象也不能直接漏给程序员。这些封装的ResultSet proxy, Statement proxy, Connection proxy等需要重载所有的方法,截取所有的异常。而且这些异常里面也许有其它的合法异常,不分青红皂白地遇到SQLException就关闭Connection好象也不是很好。


但是,这个东西做起来好象比较复杂,而且,似乎也没有连接池这么做。
adjoin 2003-11-05
  • 打赏
  • 举报
回复
学习
hyhu 2003-11-05
  • 打赏
  • 举报
回复
cbhyk()的很不错!
Yanbin_Q 2003-11-05
  • 打赏
  • 举报
回复
很多应用借用的池的概念,比如线程中有会话模型和线程池模型。
wxh512 2003-11-05
  • 打赏
  • 举报
回复
是不是数据库区驱动没装好呀
cbhyk 2003-11-05
  • 打赏
  • 举报
回复
这样实现连接池的getConnection方法:
public Connection getConnection() throws SQLException
{
while(没有超时)
{
Connection conn = 取得一个空闲的连接
try
{
用conn执行一下数据库操作(如执行一条简单的SELECT语句)
return conn; //成功,返回该连接
}
catch(SQLException e)
{
丢弃conn
e.printStackTrace();
}
}
throw new SQLException("取连接超时");
}

虽然速度慢了一点,但可以在一定程度上解决数据库重启的问题
truezerg 2003-11-05
  • 打赏
  • 举报
回复
你们的情况数据库重起以后怎么让那个系统重新工作的?
廖雪峰 2003-11-05
  • 打赏
  • 举报
回复
建议慎重选择数据库。
ajoo 2003-11-05
  • 打赏
  • 举报
回复
其实,自己写,好像也没有很好的办法。
各个jdbc driver对连接无效给出的异常没有标准。往往就是一个SQLException。让你很难区分这是一个普通的数据库异常还是因为数据库重新启动了或者fail over了。

而且,也不存在一个安全的测试异常状态的函数。isClosed()不能用,因为它只表示这个connection.close()是否被调用过。
okwuzhijun 2003-11-05
  • 打赏
  • 举报
回复
up
liutang2 2003-11-05
  • 打赏
  • 举报
回复
找个好点的例子,修改一下,用自己写的连接池会好点。

62,635

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧