一次项目实践中DBCP数据库连接池性能优化

关于数据库连接池DBCP的关注源于刚刚结束的一轮测试,测试内容是衡量某Webserver服务创建用户接口的性能。这是一款典型的tomcat应用,使用的测试工具是Grinder。DBCP作为tomcat服务器常用的数据库连接池,其性能表现直接关乎应用的性能。

1.遇到的问题

当并发量增加到100时,该接口出现瓶颈,此时TPS接近400,如下图。但是服务端CPU和内存等资源并未达到瓶颈,服务器CPU使用率仅为30%,内存使用率为40%。监控到的javaMethod慢方法为incrAppAccountsSize(),该方法的平均响应时间达206ms。该方法的功能是增加app下的用户数,每当我们在该app下创建一个用户,相应app的总用户数就会加1,缓存中响应的数据也会更新。因此该方法涉及数据库和redis的相关操作。


数据库的插入操作通常比较耗时,添加数据库监控后,该接口相关sql语句执行时间如下,可见相关操作的sql语句的平均耗时在20ms左右,然而慢方法的耗时却在200ms左右,因此性能瓶颈不在sql语句。


因此有必要分析下代码执行过程中的堆栈信息,看看慢方法涉及哪些调用。通过jstack dump出现场的堆栈信息如下。

2.数据库连接池DBCP

众所周知,数据库的最大连接数是有限的,服务端与数据库建立连接的过程也是非常消耗资源的,因此高效利用数据库连接是非常必要的。java提供了一套专门与数据库打交道的api--jdbc,基于jdbc开发人员可以实现服务端与数据库端的通信。每次执行数据库操作通常需要经历建立数据库连接、执行数据库操作、断开数据库连接三步。当数据库并发访问量大时或请求频繁时,频繁的建立连接再断开连接会给服务端带来很大的额外开销,严重制约着服务端的性能。

因此,数据库连接需要一套维护策略,基于不同的维护策略出现了很多开源的数据库连接组件,DBCP(DataBase Connection Pool)就是其中一个,也据信是目前性能最好的数据库连接组件。除了DBCP以外还有C3P0,、proxool等也是比较常见的。在一定的维护策略下,数据库连接可以实现复用以及再回收。DBCP的使用很简单引入commons-dbcp依赖,并配置好相关参数即可。DBCP的相关配置参数如下,

  • maxActive:最大连接数
  • maxIdle:最大空闲连接数
  • minIdle:最小空闲连接数
  • initialSize:初始连接数
  • minEvictableIdleTimeMillis:对连接进行有效性校验的周期

maxActive的值通常根据自己应用的峰值并发量进行设置,当并发量高于该值后超过的请求会排队等待连接释放;当并发量介于maxIdle和maxActive之间时,使用结束的连接会被立即断开,新来的请求会重新创建连接;当并发量介于minIdle和maxIdle之间时,新来的请求会从连接池充获取一个已经建立的且空闲的连接,迅速实现连接复用。因此通常情况下应用的并发量应该维持在minIdle与maxIdle之间。这样才能保证新来的请求迅速获取一个已经建立的连接。通常情况下initialSize会小于或等于minIdle当服务器重启后服务器会与数据库节点保持initialSize个连接,当并发量超多initialSize后连接数会依次上涨至minIdle、maxIdle和maxActive,当连接空闲时根据回收策略连接会被回收,并最终回落至minIdle。

3.问题分析

结合DBCP以及第一节中利用jstack dump出的堆栈信息,可见服务端一直处于连接断开的过程中。而DBCP建立连接的速度比断开连接快,因此服务端一直卡在资源释放的阶段。服务端的DBCP的相关配置如下:

  • maxActive:500
  • maxIdle:50
  • minIdle:30
  • initialSize:30

并发访问量为160,在maxIdle和maxActive之间,因此当一个连接使用结束后由于当前连接数大于maxIdle连接无法被复用会被立即断开,新来一个请求也无法获取一个空闲的连接需要重新建立一个新的连接,由于断开连接的响应时间较慢,断开连接都在等待资源的释放,大量的线程出现排队,这样就出现了通过jstack看到的,线程被block住的现象。针对这种问题有两种优化思路:

  1. 提高连接池的复用率。
  2. 增加空闲连接的数量。

通过提高连接复用率来解决这种问题的前提是数据库操作的执行速度够快,监控数据库sql执行速度在20ms左右,如果多个数据库操作排队对性能影响不大,基于这种思路可以降低maxActive的值。当并发访问量达到上限后不再分配更多的数据库连接,而是等待前一个连接使用结束,由于maxActive与maxIdle的差值变小,因此需要断开的连接的数量变少,这样可以避免由于连接的断开性能较差导致大量线程被block住的情况。将maxActive的值修改为100后,160并发创建用户的性能如下。

慢方法incrAppAccountsSize的响应时间由800多毫秒,减小到80多毫秒,通过jstack抓取现场堆栈信息可以看到有些线程被block在getConnection状态,线程获取连接出现排队,但是由于数据库操作响应时间很快,这种线程被block在getConnection状态的时间并不长。

通过增加空闲连接的数量来也可以改善频繁断开连接所带来的性能问题,由于当前并发量超过了最大空闲连接数,增加空闲连接数可以减少断开连接的数量,提高复用率。但是增加空闲连接的数量会占用宝贵的数据库连接资源,影响服务扩展。将maxIdle大小调整为200后,160并发创建用户的性能如下。

以上数据表明,通过这两种方式可以有效的提高慢方法的响应时间。通过增加空闲连接的数量带来的性能提升优于增加复用率,因为增加空闲连接的数量可以避免排队耗时。单纯的增加maxIdle的值会使得空闲连接的数量增加。并发连接也不会一直维持在maxIdle这个水平,如果当前的并发压力减小了,DBCP本身也有连接回收策略。空闲连接的回收是通过这两个参数来实现的。

  • minEvictableIdleTimeMillis:空闲连接过期时间
  • timeBetweenEvictionRunsMillis:空闲连接回收触发周期

当连接池中的空闲连接空闲了minEvictableIdleTimeMillis这么长时间后,该连接会被置为可回收状态。DBCP会按照timeBetweenEvictionRunsMillis的时间进行周期回收。最在没有额外压力的前提下终空闲连接稳定到minIdle水平。

4.总结

数据库连接池的maxIdle和maxActive之间的差值不宜不过大。如果数据库操作响应时间很快的话可以提高连接复用率,但是这么做会出现连接排队的情况。如果数据库操作的响应时间较慢可以增加空闲连接的数量。由于数据库总的连接数是一定的,单个服务的具体数据库连接配额应该根据服务的压力进行调整。

原创文章

禁止其他公众账号转载

时间: 02-22

一次项目实践中DBCP数据库连接池性能优化的相关文章

DBCP数据库连接池——可适用DB2数据库

前提:     项目导入DB2的驱动jar包 驱动包 下载 >关于DBCP DBCP(DataBase connection pool),数据库连接池.是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件.单独使用dbcp需要3个包:commons-dbcp.jar,commons-pool.jar,commons-collections.jar由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据

DBCP数据库连接池-方式2手动创建

DBCPUtils.java package com.itheima.b_dbcp; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.dbcp.BasicDataSource; public class DBCPUtils { private static BasicDataSource dataSource ; static{ try { // 手动创建连接池 dataSou

【Java EE 学习第16天】【dbcp数据库连接池】【c3p0数据库连接池】

零.回顾之前使用的动态代理的方式实现的数据库连接池: 代码: 1 package day16.utils; 2 3 import java.io.IOException; 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.Method; 6 import java.lang.reflect.Proxy; 7 import java.sql.Connection; 8 import java.sql.D

dbcp数据库连接池属性介绍

#最大连接数量:连接池在同一时间能够分配的最大活动连接的数量,,如果设置为非正数则表示不限制,默认值8 maxActive=15 #最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建,默认值0 minIdle=5 #最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制,默认值8 maxIdle=10 #初始化连接数:连接池启动时创建的初始化连接数量,默认值0 initialSize=5 #连接被

DBCP数据库连接池连接mysql数据库的时候 出现连接问题

在部署的项目的时候发现两个问题 ,第一个问题不是太容易发现 ,因为我部署的时候没问题 ,但是产品的同事在跑流程的时候总是出现一个connetException异常  自己看了半天没发现什么问题 去网上查了一下 说是msql的连接默认是八个小时 第二个问题就是 自己这边的数据库 隔一段时间不用的话 会自动断开  查了一下资料 是dbcp连接mysql时出现的这个问题  实际上两个问题算是一个问题吧 自己配置了一下 这几天没报这样的错误 !算是解决了吧!  <bean id="dataSour

DBCP数据库连接池和 c3p0数据库连接池的使用

DBCP数据库连接池 http://m.blog.csdn.net/article/details?id=47033595 c3p0数据库连接池的使用 http://blog.csdn.net/wushangjimo/article/details/12654491

解决dbcp数据库连接池错误

使用dbcp数据库连接池时报错 java.lang.NoClassDefFoundError: org/apache/commons/pool2/PooledObjectFactory原因:缺少commons-pool2-2.4.2.jarcommons-dbcp2-2.1.1.jar 原文地址:http://blog.51cto.com/59465168/2071979

java数据库连接池性能对比

这个测试的目的是验证当前常用数据库连接池的性能. testcase Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.preparedStatement("select 1"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { } rs.close(); stmt.close(); conn.close(); test c

实践中的电商前端优化

前端优化已经是一个被写烂的题材了. 虽千万人吾往矣,这里我仅分享我的一些实践经验. 欢迎一起交流 欢迎关注我的个人公众号,不定期更新自己的工作心得. 正文如下 前端性能 1. 模块化 严格来说,代码模块化并不能带来性能上的提升,但我还是将模块化提出来,因为它真的很重要,重要到几乎所有的优化都与它息息相关. 常见的模块化方案有:AMD.CMD.UMD.ES6 如何选择? 团队习惯 个人偏好 业务需要 我靠!你怎么能把业务需要放在最后一个考虑? 因为没有哪一块业务会因为使用了不同的模块化方案而产生不