一次项目实践中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住的现象。针对这种问题有两种优化思路:
提高连接池的复用率。
增加空闲连接的数量。
通过提高连接复用率来解决这种问题的前提是数据库操作的执行速度够快,监控数据库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之间的差值不宜不过大。如果数据库操作响应时间很快的话可以提高连接复用率,但是这么做会出现连接排队的情况。如果数据库操作的响应时间较慢可以增加空闲连接的数量。由于数据库总的连接数是一定的,单个服务的具体数据库连接配额应该根据服务的压力进行调整。
原创文章
禁止其他公众账号转载

一次项目实践中DBCP数据库连接池性能优化的更多相关文章
- 【Java EE 学习 16 上】【dbcp数据库连接池】【c3p0数据库连接池】
一.回顾之前使用的动态代理的方式实现的数据库连接池: 代码: package day16.utils; import java.io.IOException; import java.lang.ref ...
- Apache中配置数据库连接池(数据源)
由于基于HTTP协议的Web程序是无状态的,因此,在应用程序中使用JDBC时,每次处理客户端请求都会重新建立数据库链接,如果客户端的请求频繁的话,这将会消耗非常多的资源,因此,在Tomcat中提供了数 ...
- Jsp中使用数据库连接池.
原文 Jsp中使用数据库连接池. 1. 在tomcat服务器目录下面的conf中找到一个叫Context.xml的配置文件,在其中加入以下代码 <Resource name="jdbc ...
- Vue项目实践中的功能实现与要点
本贴记录项目实践中,各种功能的实现与技术要点,均有待改进. 路由切换的时候,显示loading动画 目前方案是: 在每个页面都手动装载一个loading组件组件的显示依赖vuex里面的一个值 , 在r ...
- 【转】PHP中被忽略的性能优化利器:生成器.md
PHP 如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生.但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP 5.5.0才引入的功能,也可以是生成器作用不是很明 ...
- tomcat项目中配置数据库连接池
1. 在项目中新建context.xml文件,不要在tomcat服务器的目录中修改context.xml(会对整个服务器生效).. 在web项目的META-INF中存放context.xml 2. ...
- 03_dbcp数据源依赖jar包,DBCP中API介绍,不同过dbcp方式使用dbcp数据库连接池,通过配置文件使用dbcp数据库连接池
DBCP数据源 使用DBCP数据源,需要导入两个jar包 Commons-dbcp.jar:连接池的实现 Common-pool.jar:连接池实现的依赖库. 导入mysql的jar包. DBC ...
- DBCP数据库连接池的使用
DBCP的简单介绍: DBCP(DataBase Connection Pool)数据库连接池,是java数据库连接池的一种,由apache开发通过数据库连接池可以让程序自动管理数据库连接的释放和断开 ...
- DBCP数据库连接池的简单使用
0.DBCP简介 DBCP(DataBase connection pool)数据库连接池是 apache 上的一个Java连接池项目.DBCP通过连接池预先同数据库建立一些连接放在内存中( ...
随机推荐
- js金额数字格式化实现代码(三位加逗号处理保留两位置小数)
工作中很常用的东西: 例1,使数字1111111变成11,111,111.00,保留两位小数. <html> <head> <script type="text ...
- NModbus类库使用
通过串口进行通信 : 1.将 NMobus 类库导入工程中,添加引用.命名空间.工程属性必须配置 为 .NET 4.0. 2.创建 SerialPort 类的一个实例,配置参数,打开串口,如: pub ...
- 浅谈压缩感知(二十一):压缩感知重构算法之正交匹配追踪(OMP)
主要内容: OMP的算法流程 OMP的MATLAB实现 一维信号的实验与结果 测量数M与重构成功概率关系的实验与结果 稀疏度K与重构成功概率关系的实验与结果 一.OMP的算法流程 二.OMP的MATL ...
- 微软BI SSIS 2012 辅助阅读博客
大家可以根据对应的视频课程名称查找相关的辅助阅读博客,有少量辅助阅读博客和视频课程讲解内容相同,大部分都是拓展总结部分.希望大家在学完每一个视频课程之后看看相关博客内容,这样可以在知识面和深度上继续得 ...
- ios开发版证书与企业证书相关文件申请安装及其使用方法
本文主要讲述以下内容: ios开发版证书的申请, 企业证书的申请, appid的创建, provision profile的生成, 开发设备devices的绑定, 以及每个证书文件之间的关系, 最后使 ...
- C# 给枚举类型增加一个备注特性
/// <summary> /// 备注特性 /// </summary> public class RemarkAttribute : Attribute { /// < ...
- 视频压缩和H264
一.视频压缩 1.1 为什么需要压缩视频? 假设一个2小时未压缩的高清视频,1920×1080p的电影,我们来计算一下他的存储容量.先介绍一下帧率(frame rate或者是 FPS)概念,也就是每秒 ...
- git合并多个提交
git合并多个提交 [时间:2016-11] [状态:Open] [关键词:git,git rebase,合并提交,commit] 0. 引言 本文是关于Git提交记录修改的方法,主要是将多个提交记录 ...
- python 函数的参数的几种类型
定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了.对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解 ...
- RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较(转)
RabbitMQ中,所有生产者提交的消息都由Exchange来接受,然后Exchange按照特定的策略转发到Queue进行存储 RabbitMQ提供了四种Exchange:fanout,direct, ...