关于数据库链接池DBCP的关注源于刚刚结束的一轮测试,测试内容是衡量某Webserver服务建立用户接口的性能。这是一款典型的tomcat应用,使用的测试工具是Grinder。DBCP做为tomcat服务器经常使用的数据库链接池,其性能表现直接关乎应用的性能。java
1.遇到的问题web
当并发量增长到100时,该接口出现瓶颈,此时TPS接近400,以下图。可是服务端CPU和内存等资源并未达到瓶颈,服务器CPU使用率仅为30%,内存使用率为40%。监控到的javaMethod慢方法为incrAppAccountsSize(),该方法的平均响应时间达206ms。该方法的功能是增长app下的用户数,每当咱们在该app下建立一个用户,相应app的总用户数就会加1,缓存中响应的数据也会更新。所以该方法涉及数据库和redis的相关操做。redis
数据库的插入操做一般比较耗时,添加数据库监控后,该接口相关sql语句执行时间以下,可见相关操做的sql语句的平均耗时在20ms左右,然而慢方法的耗时却在200ms左右,所以性能瓶颈不在sql语句。sql
所以有必要分析下代码执行过程当中的堆栈信息,看看慢方法涉及哪些调用。经过jstack dump出现场的堆栈信息以下。数据库
2.数据库链接池DBCPapi
众所周知,数据库的最大链接数是有限的,服务端与数据库创建链接的过程也是很是消耗资源的,所以高效利用数据库链接是很是必要的。java提供了一套专门与数据库打交道的api--jdbc,基于jdbc开发人员能够实现服务端与数据库端的通讯。每次执行数据库操做一般须要经历创建数据库链接、执行数据库操做、断开数据库链接三步。当数据库并发访问量大时或请求频繁时,频繁的创建链接再断开链接会给服务端带来很大的额外开销,严重制约着服务端的性能。缓存
所以,数据库链接须要一套维护策略,基于不一样的维护策略出现了不少开源的数据库链接组件,DBCP(DataBase Connection Pool)就是其中一个,也据信是目前性能最好的数据库链接组件。除了DBCP之外还有C3P0,、proxool等也是比较常见的。在必定的维护策略下,数据库链接能够实现复用以及再回收。DBCP的使用很简单引入commons-dbcp依赖,并配置好相关参数便可。DBCP的相关配置参数以下,tomcat
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之间的差值不宜不过大。若是数据库操做响应时间很快的话能够提升链接复用率,可是这么作会出现链接排队的状况。若是数据库操做的响应时间较慢能够增长空闲链接的数量。因为数据库总的链接数是必定的,单个服务的具体数据库链接配额应该根据服务的压力进行调整。
原创文章
禁止其余公众帐号转载