基础连接池配置
- initialPoolSize : 初始连接数(default 3)
- minPoolSize :最小连接数(default 3)
- maxPoolSize :最大连接数(default 15)
- maxIdleTime :最大闲置时间, 未使用的连接在被丢弃之前在连接池存活时间,单位为秒(default 0, 永久存活)
- acquireIncrement : 获取连接数量, 连接不足时,c3p0尝试一次获取新连接的个数(default 3)
连接池中的连接数量在min和max之间变化,当用户需要一个连接的时候,如果当前没有可用连接,并且连接池中连接数量未达到上限的时候,连接池会创建一个新的连接。
获取连接十分消耗资源,所以请求量很大的时候,我们应该提高每次新增连接的个数而不是每次只新加一个连接并且让客户端等待。acquireIncrement 决定了每次新增连接的个数,当然这个数量不会图片maxPoolSize的上限。
连接池中的连接在以下情况会被释放
- 连接池通过测试发现连接可以断开
- 连接池设置了最大闲置时间, 连接超过这个时间未使用
3.连接太老了...
管理连接池大小和连接生命周期
我们根据不同的需求在性能,占用空间以及可靠性之前做出权限。C3P0提供一系列参数来对"连接池在大量负载以后需要多久将连接数降至最低", "为了提高可靠性,是否主要替换连接池中的'老'连接" 等情景进行控制。
- maxConnectionAge :连接最大存活时间(default 0, 永久存活)
- maxIdleTime
- maxIdleTimeExcessConnections :超过minPoolSize的线程最大闲置时间(default 0, 永久存活)
默认情况下, 连接池中的连接不会过期。如果想让连接可以过期,maxIdleTime 和/或maxConnectionAge 都可以使用。maxConnectionAge的区别是连接从创建开始,最大的存活时间, 即使这个连接并不空闲。
在连接池从大量负载闲置下来以后,如果我们想快速清理不需要的连接,可以给maxIdleTimeExcessConnections 设置一个比maxIdleTime小很多的值,强制连接池快速将闲置连接回收。
对于这些timeout的设置的建议:“慢,慢,慢!”。连接池的概念本身就是获取一次连接,然后使用很多很多,很多次。大部分数据库允许连接保持好几个小时或以上。没有必要在几分钟内甚至更短时间回收连接。将maxConnectionAge 或者 maxIdleTime 设置为1800 (30分钟)已经是非常激进的了。对于大部分数据库来说,几个小时或许更合适。我们可以通过测试来保证连接的可靠性。唯一可能需要设置为几分钟或者更短时间的是maxIdleTimeExcessConnections。
配置连接测试
c3p0可以通过多种方式测试连接,以减少应用遇到连接失效的可能性。连接池中的连接可能有多种原因导致失效: 一些JDBC驱动特意设置timeout; 数据库或者网络有时候会挂掉;连接本身可能因为资源泄露,驱动bug或者其他原因失效。
c3p0有多种测试连接的方式
- automaticTestTable :c3p0会根据传入的名字创建一个空的测试表,通过对这个表的query进行连接测试。并且会忽略prefferredTestQuery(default: null)
-
connectionTesterClassName :ConnectionTester 或者QueryConnectionTester
的实现类, 用来进行连接测试, 一般不推荐使用 ( Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester
) - preferredTestQuery: 定义用来测试连接的query语句,一般不推荐使用
- idleConnectionTestPeriod : c3p0会测试所有的闲置但是没有checked-out的连接, 时间间隔为设置的值(default:0 不进行测试)
- testConnectionOnCheckin: 如果设置为true,在每个连接checkin的时候异步验证连接是否有效(default:false)
- testConnectionOnCheckout: 如果设置为true,在每个连接checkout的时候验证连接是否有效(default:false)
idleConnectionTestPeriod, testConnectionOnCheckout和testConnectionOnCheckin 决定"何时"测试连接。automaticTestTable, connectionTesterClassName和 preferredTestQuery 决定"怎样"测试连接。
如果使用JDBC4并且c3p0在0.9.5版本以上,建议放手让driver进行连接测试,c3p0会首先尝试使用JDBC4引入的isValid()方法进行测试,这个方法速度快并且可靠。
对于之前的版本c3p0默认在连接关联的DatabaseMetaData上使用getTables()方法,这个方法非常稳定并且适用于不同的数据库,但是比正常的query慢很多,使用它做连接测试可能会严重影响连接池性能。
在老版本中提升连接测试性能的一个简单的办法是使用preferredTestQuery 定义一个测试query。但是需要注意的是,如果在datasource初始化之前query的表不存在的话, preferredTestQuery 会出错导致测试失败。根据数据库或者driver的不同, 像select 1这种的测试语句可能(不)会提高效率。
如果自定义query效率不够好,使用automaticTestTable, c3p0会根据传入的名称建立一个空表来测试连接。
进行连接测试最可靠的时机在check-out, 但是这也最为耗时。大多数应用应该可以通过一起使用idleConnectionTestPeriod和testConnectionOnCheckin进行可靠测试,这两个都是异步的,带来更好的性能。
对有些应用来说, 性能比偶发的数据库异常更为重要。c3p0默认不进行连接测试。设置一个相对长的ideaConnectionTestPeriod, 并不在check-in/out测试则会带来高效的性能。
连接测试建议
- 如果你确定driver 支持JDBC4 Connection.isValid()方法,并且使用c3p0-0.9.5以上版本, 不要设置preferredTestQuery。
如果driver不支持这个方法(或者你不确定), 并且数据库是MySQL或者Postgres, 在preferredTestQuery中使用SELECT 1。
对于其他数据库,参照这里的一些建议。
2.开始直接设置testConnectionOnCheckout=true, 保证程序运行正确和稳定。如果性能让你满意,那就可以保持这个设置了,这是最简单最可靠测试连接的方法。但是它确实有很大的性能开销(客户端可以感知的到)。
3.如果想减少连接检查带来的性能开销:
a. 设置testConnectionOnCheckout为false
b. 设置testConnectionOnCheckin为true
c. 设置idleConnectionTestPeriod 为30(秒),启动程序进行测试。这个是一个很稳定的设置,每30秒所有的连接在check-in的时候都会进行测试。程序应该很少会碰到关闭或者陈旧的连接,并且连接池应该可以从数据库关闭和重启后迅速恢复。但是一直这样测试还是会有一些开销。
d.如果数据库很少进行重启,那么可以考虑增大idleConnectionTestPeriod间隔,比如300,然后测试看看客户端是否会遇到关闭的连接。如果没有,那么定格在300或者可以试更大的数字。然后考虑将testConnectionOnCheckin设置回false来减少check-in检查。反之,如果遇到关闭的连接,可以考虑减少idleConnectionTestPeriod的值,将testConnectionOnCheckin设置回true。
参数的设置取决于对性能和可靠性的权衡,具体的数字并不重要,这里没有一个准确的“最佳值”,而是需要进行测试。
选取2 还是3 取决于客户端在check connection out以后的工作量,如果客户端经常执行复杂查询或者操作,一个很快的连接检查不会有助于整体性能的提升。但是如果客户端check connection out以后的操作很简单,那么附加一个额外的检查会明显拖慢性能。
理论上看起来很美好,但是我们往往很难进行权衡。推荐的做法是测试3,看看是否会提高性能,是否会遇到关闭的连接,在数据库重启的情况下是否能够恢复,再做出决定。
但是一定要保证测试本身的性能,JDBC是否支持isValid, 或者我们是否有一个合适的preferredTestQuery
配置Statement池
c3p0实现了JDBC定义的PreparedStatement的池. 有些时候,使用statement池可以显著提高应用性能。但是有的时候statement池带来的开销可能会轻微影响性能。是否使用,怎么配置取决于数据库的解析,执行计划,和对query语句的优化(对于prepared statement)。不同的数据库和不同的JDBC驱动在这方面表现迥异。将不使用statement池的性能作为基准来测试它可以带来的性能提升是一个不错的方法。
statement池对应的参数有以下几种:
maxStatements 是JDBC中用来控制statement池的标准参数。表示DataSource中最多可以缓存的PreparedStatement的数量。当达到上限的时候,pool会移除LRU(最近最少使用的) statement。这听起来十分简单,但是细想是一个很奇怪的行为。因为被缓存的statements从概念上来说分属各自的连接,并不是全局资源。为了找到一个合适的maxStatements大小,不会"毁掉"被缓存的statements,我们需要考虑经常使用的PreparedStatement的数量,并且需要乘以连接池中连接的数量(连接池很忙的话可能达到maxPoolSize)。
maxStatementsPerConnection是一个非标准的配置参数, 但是看起来更合理一些。 它定义了每个connection允许的PreparedStatement的最大数量。我们可以将这个值设置的比我们应用中的"常用statements"的数量稍微大一些。
上述任何一个参数如果大于0, statement池就会开启。这两个设置是分别生效的,彼此不会影响。
如果statementCacheNumDeferredCloseThreads大于0, statement池会推迟物理关闭缓存的statements的时机,直到它所在的connection不再被使用。在一些JDBC driver中(特别是Oracle), connection在使用的时候是不会关闭它拥有的statement的。这个参数默认值是0,如果发现有和connection 关闭任务相关的"明显的死锁",我们可以尝试设置一个正数,绝大多数情况这个值应该是1。如果需要多余1个线程来做statement销毁的工作,那么 我们可能需要调大maxStatements或者maxStatementsPerConnection, 而不是调大statementCacheNumDeferredCloseThreads。
网友评论