闲聊DB连接池
2019-04-17 本文已影响0人
樊明幸
前言
最近需要调研一下数据库连接池,网上相关的文章很多,有不少大拿也针对主流连接池做了各种对比,hikariCP基本上是稳坐头把交椅,为了真正体验下效果,决定自己动手,来一次和连接池的亲密接触。
性能测试
本次测试连接池涉及hikariCP、druid、dbcp、dbcp2、c3p0,针对jdk1.7 和 jdk1.8分别做了性能测试。测试场景主要模拟公司内部核心交易系统创建支付单和支付结果等真实流程,测试数据模拟生产真实数据。话不多说,贴测试报告。
环境配置
环境 | 配置 |
---|---|
CPU | Core 4,Inter(R) Xeon(R) CPU E3-1225 v5 @ 3.30GHz |
RAM | 8G |
OS | WIN7企业版 |
MYSQL | 5.6 (测试用例和mysql在同一台机器上面,避免网络IO影响) |
连接池版本
连接池 | java7 | java8 |
---|---|---|
hikariCP | 2.4.9 | 3.3.1 |
druid | 1.1.6 | 1.1.6 |
dbcp | 1.4 | 1.4 |
dbcp2 | 2.4.0 | 2.6.0 |
c3p0 | 0.9.1.2 | 0.9.1.2 |
性能对比
连接池连接数10-10,borrow和return均不心跳检测,由于java7和java8测试结果相近,此处只贴了java8
1.串行创建支付单场景(一个事务写3张表),预热完成后,每个连接池循环10000次调用,测试20轮

2.串行创建支付结果场景(单表),预热完成后,每个连接池循环10000次调用,测试20轮

3.并行创建支付单场景(一个事务写3张表),预热完成后,每个连接池并发10000次调用(10线程并发),测试20轮

4.并行创建支付结果场景(单表),预热完成后,每个连接池并发10000次调用(10线程并发),测试20轮

5.串行查询支付单场景(单表,唯一索引),预热完成后,每个连接池循环10000次调用,测试20轮

6.并行查询支付单场景(单表,唯一索引),预热完成后,每个连接池并发10000次调用(10线程并发),测试20轮

纳尼?!?!?!?
看到这儿我有点儿不淡定了,性能无敌的hikariCP去哪了???
是不是测试的方法不对?我们换种方式再测试下。
7.串行针对每个连接池进行获取链接、关闭连接操作,预热完成后,每个连接池循环10000次,测试20轮

这次差异比较明显,druid耗时比hikariCP略高,差异不大,相比于c3p0和dbcp2 差异就比较大了,我们再来看下并发场景。
8.并行针对每个连接池进行获取链接、关闭连接操作,预热完成后,每个连接池并发10000次调用(10线程并发),测试20轮

这次测试结果就比较明显了,hikariCP在第8轮轮测试中完胜。
9.并发写支付结果,每个连接池10个线程并行入库20w次,观察不同连接池进程耗费的资源情况。





从上面的指标可以看出,druid 和 hikariCP cpu使用率,相比其他三款连接池确实要低一些(5-10个点)
粗略的看了下各个连接池的源码,hikariCP确实很轻量,其中为了更好的控制并发,hikariCP封装了ConcurrentBag,实现了比LinkedBlockingQueue、LinkedTransferQueue更好的并发性能。ConcurrentBag内部同时使用了ThreadLocal和CopyOnWriteArrayList来存储db连接,首先尝试从ThreadLocal中获取属于当前线程的元素来避免锁竞争,如果没有可用元素则再次从共享的CopyOnWriteArrayList中获取。并且使用FastList替代ArrayList(存放Statement),get和remove方法精简了不少,避免每次get()调用都要进行range check,避免调用remove()时的从头到尾的扫描。
druid 代码量明显多了不少,底层是用数组存放连接,ArrayList存放statement,使用ReenTrantLock控制并发。
测试结论
经过多轮测试发现,其实不论是druid或是hikariCP,它们都足够稳定、高效,连接池本身的性能差异实际在应用运行过程中所体现除的差异是微乎其微的,因为操作数据库大部分的耗时并不在连接池本身,而在于SQL,在于IO,一般情况下不存在因为连接池的自身的实现机制导致系统性能下降的情况。那我们在选择连接池的时候,所关注的就不仅仅是性能了,还要看是否在持续更新,有什么特性(比如druid功能全面,易于扩展,监控完善等),存在哪些坑(c3p0存在deadlock风险),极端情况处理方式(数据库挂了)等等。