数据库连接池简析

[b]序言[/b]


我参与的这些项目都用到了数据库连接池,这自然是有它的原因的。有时候我们可能会忘了当初为什么使用了某种设计模式或者某项技术,因此很有必要从头再推理一遍。每项技术或者技术决策肯定都有它的优势和劣势,如果发现它没有缺点的话,那你最好仔细想想是不是漏掉了什么。

[b]数据库连接的生命周期[/b]

数据库的每一个读写操作都需要有一个连接。我们来看下数据库连接的调用流是什么样的:

[img]http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/wp-content/uploads/2014/04/connectionlifecycle.gif[/img]

调用流程是这样的:

1. 应用程序的数据访问层请求DataSource来获取一个数据库连接。
2. DataSource使用数据库驱动来打开一个数据库连接。
3. 创建数据库连接,同时打开了一个TCP socket。
4. 应用程序进行数据库的读写。
5. 连接已经不再需要了,因此关闭它。
6. 关闭socket。

很容易可以看到,数据库连接的打开和关闭是非常昂贵的。PostgreSQL会为每个客户端连接分配[url=http://www.postgresql.org/docs/9.3/static/connect-estab.html]一个单独的操作系统进程[/url],因此高频率的打开关闭操作会使你的数据库管理系统负担很重。

重用数据库连接最主要的原因是:

1. 减少应用程序与数据库之间创建/销毁TCP连接的开销
2. 减少JVM的垃圾对象。

[b]池还是非池[/b]

我们来将不用连接池的实现和[url=http://brettwooldridge.github.io/HikariCP/]HikariCP[/url]进行对比,HikariCP应该是最高效的连接池框架了。

测试程序会创建并关闭1000个连接。


private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceConnectionTest.class);

private static final int MAX_ITERATIONS = 1000;

private Slf4jReporter logReporter;

private Timer timer;

protected abstract DataSource getDataSource();

@Before
public void init() {
MetricRegistry metricRegistry = new MetricRegistry();
this.logReporter = Slf4jReporter
.forRegistry(metricRegistry)
.outputTo(LOGGER)
.build();
timer = metricRegistry.timer("connection");
}

@Test
public void testOpenCloseConnections() throws SQLException {
for (int i = 0; i < MAX_ITERATIONS; i++) {
Timer.Context context = timer.time();
getDataSource().getConnection().close();
context.stop();
}
logReporter.report();
}



图中显示的是打开及关闭连接所花费的时间,当然这个时间越短则越好。

[img]http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/wp-content/uploads/2014/04/nopoolingvsconnectionpooling1.png[/img]

使用了连接池的实现要比没有连接池快600倍。我们的企业级系统中有大量的应用,光是一个批处理的系统每小时就会创建两百万的数据库连接,因此像这样两个数量级差距的优化当然是应该考虑的。


[table]

|类型|不使用连接池的情况|使用了连接池的情况

|最短时间|74.551414|0.002633
|最长时间|146.69324|125.528047
|平均时间|78.216549|0.128900
|标准差|5.9438335|3.969438
|中位数|76.150440|0.003218

[/table]

[b]为什么连接池如此高效?[/b]

要明白为什么连接池的性能会这么好,我们需要分析下连接池的调用流:

[img]http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/wp-content/uploads/2014/04/poolingconnectionlifecycle.gif[/img]

每当请求一个连接的时候,使用了连接池的数据源都会先通过连接池来获取一个新的连接。连接池只有当没有可用的连接并且还没有达到连接池上限的时候才会去创建新的连接。而连接池的close()方法只是把连接扔回到池里而已,并不是真的要关闭它。

[img]http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/wp-content/uploads/2014/04/connectionacquirerequeststates.gif[/img]

[b]更快,更安全[/b]

连接池扮演了连接请求的一个有界缓冲区的角色。如果流量瞬间出现了抖动连接池会使它变得平缓,而不是去耗尽所有的数据库资源。

等待超时的机制就像一个安全挂钩,它避免数据库服务器出现过高的负载。如果有个应用想要使用了过多的数据库资源,连接池会减缓它的调用,以免它将数据库压垮了(这样整个企业系统都会受到影响)。


[b]能力越大,责任越大[/b]

所有的这些好处都是有代价的,连接池的配置又带来了额外的复杂性(尤其在大型的企业级应用中)。因此有了它并不能就高枕无忧了,你还得去注意连接池的许多配置项,比如:

1. 连接的最小数量
2. 连接池的最大数量
3. 最长空闲时间
4. 获取连接超时时间
5. 超时的重试次数

我的下一篇文章将会深入介绍企业级应用中数据库连接池面临的一个挑战,同时介绍下[url=https://github.com/vladmihalcea/flexy-pool]Flexy Pool[/url]是如何帮助你找到合适的连接池大小的。

代码在[url=https://github.com/vladmihalcea/vladmihalcea.wordpress.com/tree/master/db-facts]Github[/url]上可以下载。
原创文章转载请注明出处:[url=http://it.deepinmind.com/db/2014/05/04/the-anatomy-of-connection-pooling.html]http://it.deepinmind.com[/url]

[url=http://vladmihalcea.com/2014/04/17/the-anatomy-of-connection-pooling/]英文原文链接[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值