摘要:最近针对某系统做了次压力测试,在并发较高的时候暴露了一个maxIdle设置不合理的问题,特总结一下这次问题,并分析一下这个问题背后的原因。
问题现象
用60个线程并发访问某个读和写均有的业务接口,性能测试人员查看Oracle数据库负载CPU非常高,而且oracle的Logons指标达到148.1次/秒(这个值的含义是数据库客户端登录认证的每秒的次数)。
而且查看应用程序中的线程栈,也发现了较多数量的线程处于创建新的oracle物理连接处。
这当时就让我有点儿糊涂了,我们不是用了连接池吗?怎么还会经常创建物理连接呢?
问题分析解决
遇到这个问题,我当时也是从自己掌握的各种知识里去想可能的原因,都想到了是不是因为tcp连接的超时时间是不是太短,仔细一想这些可能性都排除掉了。然后查看连接池的配置,当时我们的同学告诉我这些配置跟线上的是一样的,应该没有问题,我当时是就怀疑是maxIdle设置太小导致的原因,但是没有什么具体的依据。
所以我就简单开发了一个页面可以实时查看当时应用连接池的active number(当前被使用的数据库连接数)和idle number(当前处于空闲状态的数据库连接数)。再次运行测试脚本,通过查看连接池的连接数就可以看出来,连接数的波动较大,有时候连接总数(上面两个数字相加)从20多突然降到10,说明物理连接被销毁了,创建了新的物理连接,那这就与我们看到的一些异常现象相吻合了,创建新的连接就会导致oracle数据库服务器的logons数量增加。由于连接的复用率较差,导致频繁的创建物理连接,oracle数据库的资源消耗增大。
网上找到一篇文章详细介绍dbcp连接池配置的,它关于maxIdle的配置说明是这么写的。
maxIdle值与maxActive值应配置的接近。 因为,当连接数超过maxIdle值后,刚刚使用完的连接(刚刚空闲下来)会立即被销毁。而不是我想要的空闲M秒后再销毁起一个缓冲作用。这一点DBCP做的可能与你想像的不一样。 若maxIdle与maxActive相差较大,在高负载的系统中会导致频繁的创建、销毁连接,连接数在maxIdle与maxActive间快速频繁波动,这不是我想要的。 高负载系统的maxIdle值可以设置为与maxActive相同或设置为-1(-1表示不限制),让连接数量在minIdle与maxIdle间缓冲慢速波动。
看到这个我明白了因为我们的配置是maxIdle配置的值是5,而maxActive配置的值为40,这样当并发较高的时候,当连接数接近maxActive值的情况下,空闲连接数很容易超过maxIdle,很快就被连接池给主动销毁了,这样就导致了连接频繁的创建,弱化了数据库连接池的作用。
这次的性能测试然让我深刻的感受到了数据库连接池对的价值,使用得当,它能够很好地复用已有的物理连接,在高并发的场景下,减少频繁的创建和销毁物理连接,降低系统的压力。用得不好,它的价值就发挥不出来,就像咱们今天这个案例一样。
为什么是这样的?
maxIdle的值为什么要与maxActive的接近呢?果真如此吗?我们还要通过源码来分析它背后的根本原因。
看看上图是对dbcp连接池的获取和归还连接对象的流程图的描述,通过该图我们就一目了然了。
maxIdle配置总结
maxIdle值与maxActive值应配置的接近。
因为,当连接数超过maxIdle值后,刚刚使用完的连接(刚刚空闲下来)会立即被销毁。而不是我想要的空闲M秒后再销毁起一个缓冲作用。这一点DBCP做的可能与你想像的不一样。
若maxIdle与maxActive相差较大,在高负载的系统中会导致频繁的创建、销毁连接,连接数在maxIdle与maxActive间快速频繁波动,这不是我们想要的。
高负载系统的maxIdle值可以设置为与maxActive相同或设置为-1(-1表示不限制),让连接数量在minIdle与maxIdle间缓冲慢速波动。多余的空闲连接等待回收线程来缓慢回收。
至于具体多少合理取决于大家的具体应用场景,但是配置的原则是这样的:
1.如果是连接池命中率(即应用在获取连接时直接获得已经创建物理连接的比率)优先的应用。适合连接资源较充裕的环境,可以尽可能将maxIdle设置得更大一些,多数应用设置的大一些会更加安全。
2.如果是连接资源利用率(即被使用连接占的比率)优先的应用。适合那些连接资源紧张的环境。可以尽可能将maxIdle设置得更小一些,这样空闲连接能够得到更快的释放,保持一个较小的连接,但是设置的过小的话会导致连接池命中率非常低,弱化连接池的作用。