关于数据库连接池的几个参数的几个补充
Posted Qunar技术沙龙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于数据库连接池的几个参数的几个补充相关的知识,希望对你有一定的参考价值。
前几天分享了一篇《》(点击阅读)后和公司几个同事讨论了一下,觉得有几个细节需要更深入细化一下。
有同事看到tomcat-jdbc的testOnBorrow默认并不是每次拿连接的时候都检查很诧异,认为这里是一个坑;testOnBorrow还受validationInterval限制,而validationInterval默认设置为30秒,但是根据业务的重要等级需要认真调整。比如对于最关键的业务,甚至需要将validationInterval设置为0,这样就每次拿连接都检查。因为一般来讲,网络波动可能是大面积的,一个应用实例会有多个连接,而一个系统会部署多个应用实例,这样出问题的时候影响面还是挺大的(可能造成这个期间所有请求都失败)。最后大家认可的一点是对于面向终端用户的场景,testOnBorrow都应该打开,并且validationInterval设置为0,对于后端一些异步场景,validationInterval可以设置大一些,另外大家觉得每次都检查带来的可靠性和所增加的开销相比是值得的,数据库最大的压力是来自业务SQL的慢查询。
tomcat-jdbc的testWhileIdle除了受validationInterval控制外,还受timeBetweenEvictionRunsMillis参数控制。
Durid连接池的testOnBorrow是每次拿连接都会check,没有interval设置。我在前文里提到Durid因为对于业务SQL也执行有效性检查,可以不设置testOnBorrow,有同事并不可认可这个说法,觉得Durid虽然比其他连接池更优,对于业务SQL也会检查是否失效,但是对于最关键的应用还是应该检查,如果不检查还是会碰到网络恢复后首次失败的情况,这对于一些关键场景(critical path)最好要避免。
另外,我们再看看其他几个连接池对于这些参数的考虑
mysql .net connector
mysql .net connector其实不算连接池,它是数据库驱动这一层,但是在.net里驱动一般就自带了连接池。而mysql .net connector每次从池里拿连接都会进行有效性测试,它使用的是数据库的ping包进行测试。
c3p0
c3p0是一个老牌数据库连接池,它的参数不叫testXxx,是另外一套参数:testConnectionOnCheckout, testConnectionOnCheckin, idleConnectionTestPeriod,preferredTestQuery和connectionTesterClassName。testConnectionOnCheckout如果设置则每次拿连接的时候都会检测,但是c3p0的检测机制是可以自定义(connectionTesterClassName),默认会使用com.mchange.v2.c3p0.impl.DefaultConnectionTester。而且c3p0可以不指定测试的query(preferredTestQuery),如果不指定测试query,它会利用jdbc connection的isValid检测(对于mysql driver就是发送一个ping命令)。testConnectionOnCheckin是归还连接时测试,如果设置则每次归还都检测。idle检测受idleConnectionTestPeriod控制,默认不检测。
HikariCP
这是号称世界上最牛逼的jdbc connection pool(具体我没有测试过)。这个连接池没有testXxx的配置,只有一个connectionTestQuery,如果你配置了这个query则每次拿连接的时候用这个query测试,否则就使用java.sql.Connection的isValid测试。这个线程池在每次从池里拿连接都会进行有效性检测,没有开关可以关闭。
至于它为啥号称更快(作为连接池本身来说,要提高性能其实就少减少锁的争用),大致看了一下是实现了一个ConcurrentBag数据结构。里面使用了ThreadLocal,默认会从ThreadLocal里取连接,如果取不到则去一个全局的结构(sharedList, CopyOnWriteArrayList,因为这个连接池归还是归还到ThreadLocal里,所以对于sharedList是读多写少的,用CopyOnWriteArrayList可以减少读取时锁的竞争)里取,而且从这个sharedList里取并不会从中移除,而是只做一个标记。去全局结构里取的时候还会增加一个计数,如果还取不到则去一个SynchronousQueue里取,我们都知道这个queue其实就是一个transfer(左手倒右手),而在归还的时候会判断这个计数,计数不为零则说明有线程在等待池,立即将其丢给SynchronousQueue,如果没有计数则放到threadLocal里。另外它使用一个线程池在不断地创建必要(也就是有条件的)的连接,创建的连接就是放在sharedList里,所以这个池子里所有的连接都会在sharedList里,是否空闲是看一个标记,另外大部分连接可能就在ThreadLocal里,整个结构可以做到无锁的,只需要原子操作这个标记即可,而且如果线程之间比较均衡,则对这个原子操作的争抢也应该很低。
另外,HikariCP也会根据业务SQL的异常判断是否要从连接池剔除连接。
BoneCP
BoneCP也号称它是最快的数据库连接池(同样没做性能测试,只看看它的一些策略)。BoneCP我就没有看到类似testOnBorrow的设置,也没看到它在从池里拿连接的时候有任何测试,但是可以配置idleConnectionTestPeriodInSeconds和connectionTestStatement执行空闲检查。但是BoneCP在执行业务SQL的时候,如果遇到异常会对异常进行判断,如果是连接失效的异常会将这个连接从连接池里移除,而如果是有些不太确定是不是连接失效的异常会给连接设置一个possiblyBroken的状态,当连接归还的时候如果这个状态设置了会进行连接有效性测试。
BoneCP连接池的主要实现算法是将连接分为多个桶,每个桶是一个BlockingQueue,获取连接的时候对当前thread id取模,看看落到哪个桶,然后去对应的桶取,这样可以降低锁竞争的几率。
总结
tomcat-jdbc: 提供了testOnBorrow开关,但受validationInterval控制,如果要求网络或数据库恢复后没有任何业务失败,可以将validationInterval设置为0,强制每次都检查,默认会使用validateQuery检查,但是用户可以提供自己的validator。如果关闭所有testXxx开关,则无论业务如何失败都不会认为是连接失效。
durid: 打开testOnBorrow开关后每次都会检查。对于业务SQL失败会对异常进行判断,以确定连接是否失效。
c3p0: 提供了testConnectionOnCheckout开关,如果打开后每次都会检查,提供了两种检查方式:jdbc validate和validate query。对于业务SQL失败不认为是连接失效。
HikariCP: 强制每次拿连接都会检查,没有提供validate query的时候使用jdbc validate检查(推荐)。对于业务SQL失败会对异常进行判断,进而判断连接是否失效。
BoneCP: 拿连接时不会进行检查,也没有开关控制。但是对于业务SQL执行失败会对异常进行判断,以确认连接是否失效,并且对于其他一些连接不能完全确认失效的异常会做标记,返回连接到连接池时会对标记了的连接进行检查。
推荐使用方式:对于critical应用不推荐使用BoneCP,对于critical应用对于tomcat-jdbc建议打开testOnBorrow,并且将validationInterval设置为0。对于durid建议打开testOnBorrow,对于c3p0建议打开testConnectionOnCheckout。HikariCP是默认就检测。
以上是关于关于数据库连接池的几个参数的几个补充的主要内容,如果未能解决你的问题,请参考以下文章