五大理由分析Springboot 2.0为何选择HikariCP

五大理由分析Springboot 2.0为何选择HikariCP

2018-05-04 工匠小猪猪 占小狼的博客

本文非原创,是工匠小猪猪的技术世界搜集了一些HikariCP相关的资料整理给你们的介绍,主要讲解了为何sb2选择了HikariCP以及HikariCP为何这么快。html

更多关于HikariCP的内容,能够搜索"工匠小猪猪的技术世界"公众号进行关注mysql

Springboot2默认数据库链接池选择了HikariCP

默认的数据库链接池由Tomcat换成HikariCP. 若是在一个Tomcat应用中用spring.datasource.type来强制使用Hikari链接池, 则能够去掉这个override.git

为什么选择HikariCP

HiKariCP是数据库链接池的一个后起之秀,号称性能最好,能够完美地PK掉其余链接池,是一个高性能的JDBC链接池,基于BoneCP作了很多的改进和优化。其做者还有另一个开源做品——高性能的JSON解析器HikariJSON。github

它,超快,快到连Spring Boot 2都宣布支持了。spring

代码体积更是少的可怜,130kb。sql

https://github.com/brettwooldridge/HikariJSON数据库

为什么要使用HiKariCP?这要先从BoneCP提及:
什么?不是有C3P0/DBCP这些成熟的数据库链接池吗?一直用的好好的,为何又搞出一个BoneCP来?由于,传说中BoneCP在快速这个特色上作到了极致,官方数据是C3P0等的25倍左右。不相信?其实我也不怎么信。但是,有图有真相啊(图片来自BoneCP官网:http://jolbox.com/benchmarks.html):数组

从上述结果能够看出HikariCP的性能远高于c3p0、tomcat等链接池,以至后来BoneCP做者都放弃了维护,在Github项目主页推荐你们使用HikariCP。另外,Spring Boot将在2.0版本中把HikariCP做为其默认的JDBC链接池。缓存

PS:须要指出的是,上图中的数据是HikariCP做者对各个链接池调用DataSource.getConnection()、Connection.close()、Connection.prepareStatement()、Statement.execute()、Statement.close()方法的性能测试结果。tomcat

并且,网上对于BoneCP是好评如潮啊,推荐的文章一搜一大堆。

然而,上Maven Repository网站(http://mvnrepository.com/artifact/com.jolbox/bonecp)查找有没有最新版本的时候,你会发现最新的是2013年10月份的(这么久没新版本出来了?)。因而,再去BoneCP的Githut(https://github.com/wwadge/bonecp)上看看最近有没有提交代码。却发现,BoneCP的做者对于这个项目貌似已经心灰意冷,说是要让步给HikariCP了(有图有真相):

……什么?又来一个CP?……什么是Hikari?
Hikari来自日文,是“光”(阳光的光,不是光秃秃的光)的意思。做者估计是为了借助这个词来暗示这个CP速度飞快。不知做者是否是日本人,不过日本也有不少优秀的码农,据说比特币听说日本人搞出来的。。。

这个产品的口号是“快速、简单、可靠”。实际状况跟这个口号真的匹配吗?又是有图有真相(Benchmarks又来了):

这个图,也间接地、再一次地证实了boneCP比c3p0强大不少,固然,跟“光”比起来,又弱了很多啊。

那么,这么好的是怎么作到的呢?官网详细地说明了HikariCP所作的一些优化,总结以下:

  • 字节码精简 :优化代码,直到编译后的字节码最少,这样,CPU缓存能够加载更多的程序代码;

  • 优化代理和拦截器:减小代码,例如HikariCP的Statement proxy只有100行代码,只有BoneCP的十分之一;

  • 自定义数组类型(FastStatementList)代替ArrayList:避免每次get()调用都要进行range check,避免调用remove()时的从头至尾的扫描;

  • 自定义集合类型(ConcurrentBag:提升并发读写的效率;

  • 其余针对BoneCP缺陷的优化,好比对于耗时超过一个CPU时间片的方法调用的研究(但没说具体怎么优化)。

不少优化的对比都是针对BoneCP的……哈哈。
(参考文章:https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole)

 

理由1、代码量

几个链接池的代码量对比(代码量越少,通常意味着执行效率越高、发生bug的可能性越低):

 

理由2、口碑

但是,“黄婆卖瓜,自催自擂”这个俗语日本人也是懂得,因而,用户的好评如潮也是有图有真相:

 

理由3、速度

还有第三方关于速度的测试:

理由4、稳定性

也许你会说,速度高,若是不稳定也是硬伤啊。因而,关于稳定性的图也来了:

理由5、可靠性

另外,关于可靠性方面,也是有实验和数据支持的。对于数据库链接中断的状况,经过测试getConnection(),各类CP的不相同处理方法以下:
(全部CP都配置了跟connectionTimeout相似的参数为5秒钟)

  • HikariCP:等待5秒钟后,若是链接仍是没有恢复,则抛出一个SQLExceptions 异常;后续的getConnection()也是同样处理;

  • C3P0:彻底没有反应,没有提示,也不会在“CheckoutTimeout”配置的时长超时后有任何通知给调用者;而后等待2分钟后终于醒来了,返回一个error;

  • Tomcat:返回一个connection,而后……调用者若是利用这个无效的connection执行SQL语句……结果可想而知;大约55秒以后终于醒来了,这时候的getConnection()终于能够返回一个error,但没有等待参数配置的5秒钟,而是当即返回error;

  • BoneCP:跟Tomcat的处理方法同样;也是大约55秒以后才醒来,有了正常的反应,而且终于会等待5秒钟以后返回error了;

可见,HikariCP的处理方式是最合理的。根据这个测试结果,对于各个CP处理数据库中断的状况,评分以下:

参考文章:https://github.com/brettwooldridge/HikariCP/wiki/Bad-Behavior:-Handling-Database-Down

HikariCP为何这么快

JDBC链接池的实现并不复杂,主要是对JDBC中几个核心对象Connection、Statement、PreparedStatement、CallableStatement以及ResultSet的封装与动态代理。接下来从几个方面来看看HikariCP为何这么快:

优化并精简字节码

HikariCP利用了一个第三方的Java字节码修改类库Javassist来生成委托实现动态代理。动态代理的实如今ProxyFactory类,源码以下:

发现这些代理方法中只有一行直接抛异常的代码,注释写着“Body is replaced (injected) by JavassistProxyFactory”,其实方法body中的代码是在编译时调用JavassistProxyFactory才生成的,主要代码见下图:

之因此使用Javassist生成动态代理,是由于其速度更快,相比于JDK Proxy生成的字节码更少,精简了不少没必要要的字节码。

ConcurrentBag:更好的并发集合类实现

ConcurrentBag的实现借鉴于C#中的同名类,是一个专门为链接池设计的lock-less集合,实现了比LinkedBlockingQueue、LinkedTransferQueue更好的并发性能。ConcurrentBag内部同时使用了ThreadLocal和CopyOnWriteArrayList来存储元素,其中CopyOnWriteArrayList是线程共享的。ConcurrentBag采用了queue-stealing的机制获取元素:首先尝试从ThreadLocal中获取属于当前线程的元素来避免锁竞争,若是没有可用元素则再次从共享的CopyOnWriteArrayList中获取。此外,ThreadLocal和CopyOnWriteArrayList在ConcurrentBag中都是成员变量,线程间不共享,避免了伪共享(false sharing)的发生。

使用FastList替代ArrayList

FastList是一个List接口的精简实现,只实现了接口中必要的几个方法。JDK ArrayList每次调用get()方法时都会进行rangeCheck检查索引是否越界,FastList的实现中去除了这一检查,只要保证索引合法那么rangeCheck就成为了避免必要的计算开销(固然开销极小)。此外,HikariCP使用List来保存打开的Statement,当Statement关闭或Connection关闭时须要将对应的Statement从List中移除。一般状况下,同一个Connection建立了多个Statement时,后打开的Statement会先关闭。ArrayList的remove(Object)方法是从头开始遍历数组,而FastList是从数组的尾部开始遍历,所以更为高效。

HikariCP与Druid相比哪一个更好?

有些用户给了druid这样的评论:

不评论,一个追求性能,一个偏向监控,直接看以前有人给HikariCP提的关于跟Druid对比分析的issue吧。HikariCP做者对Druid作了测试并给出了测试结果数据,Druid做者温少也对此做了评论。Issue连接:

https://github.com/brettwooldridge/HikariCP/issues/232

笔者我的的观点是,hikariCP能够提供监控功能的,好比metrics,能够参见笔者的这篇文章 【追光者系列】HikariCP链接池监控指标实战
另外,监控方面,skywalking、pinpoint、mycat这些agent也是能够作到的,之后service mesh普及了更加能够监控了,好比sharding-jdbc也能够作监控,datamesh,sidecar也能够作监控的。

Springboot2快速上手

说得这么好,用起来会不会很麻烦啊,会不会有不少参数要配置才能有这样的效果啊?答案是:不会。

springboot 2.0 默认链接池就是Hikari了,因此引用parents后不用专门加依赖

配置一下就好

# jdbc_config   datasource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/datebook?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull
spring.datasource.username=root
spring.datasource.password=root
# Hikari will use the above plus the following to setup connection pooling
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1

直接启动便可 如图

 

相关文章
相关标签/搜索