为什么HikariCP被号称为性能最好的Java数据库连接池,如何配置使用

转自:https://blog.csdn.net/clementad/article/details/46928621 

 

HiKariCP 是数据库连接池的一个后起之秀,号称性能最好,可以完美地 PK 掉其他连接池。

 

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

 

 

而且,网上对于 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 强大很多,当然,跟“光”比起来,又弱了不少啊。

那么,这么好的 P 是怎么做到的呢?官网详细地说明了 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)

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

 

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


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


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

另外,关于可靠性方面,也是有实验和数据支持的。对于数据库连接中断的情况,通过测试 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


说得这么好,用起来会不会很麻烦啊,会不会有很多参数要配置才能有这样的效果啊?答案是:不会。
如果之前用的是 BoneCP 配置的数据源,那么,就简单了,只需要把 dataSource 换一下,稍微调整一下参数就行了:

BoneCP 的数据源配置:

<!-- BoneCp Datasource -->
 <bean id="dataSourceBoneCp" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
  <property name="driverClass" value="${db.driverClass}" />
  <property name="jdbcUrl" value="${db.url}" />
  <property name="username" value="${db.username}" />
  <property name="password" value="${db.password}" />
  <property name="idleConnectionTestPeriodInMinutes" value="2" />
  <property name="idleMaxAgeInMinutes" value="2" />
  <property name="maxConnectionsPerPartition" value="2" />
  <property name="minConnectionsPerPartition" value="0" />
  <property name="partitionCount" value="2" />
  <property name="acquireIncrement" value="1" />
  <property name="statementsCacheSize" value="100" />
  <property name="lazyInit" value="true"/>
  <property name="maxConnectionAgeInSeconds" value="20"/>
  <property name="defaultReadOnly" value="true"/>
 </bean>

 

HiKariCP 的数据源配置:

<!-- Hikari Datasource -->
 <bean id="dataSourceHikari" class="com.zaxxer.hikari.HikariDataSource"  destroy-method="shutdown">
  <!-- <property name="driverClassName" value="${db.driverClass}" /> --> <!-- 无需指定,除非系统无法自动识别 -->
  <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8" />
  <property name="username" value="${db.username}" />
  <property name="password" value="${db.password}" />
   <!-- 连接只读数据库时配置为 true, 保证安全 -->
  <property name="readOnly" value="false" />
  <!-- 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生 SQLException, 缺省:30 秒 -->
  <property name="connectionTimeout" value="30000" />
  <!-- 一个连接 idle 状态的最大时长(毫秒),超时则被释放(retired),缺省:10 分钟 -->
  <property name="idleTimeout" value="600000" />
  <!-- 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30 分钟,建议设置比数据库超时时长少 30 秒,参考 MySQL wait_timeout 参数(show variables like '%timeout%';) -->
  <property name="maxLifetime" value="1800000" />
  <!-- 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count) -->
  <property name="maximumPoolSize" value="15" />
 </bean>

其中,很多配置都使用缺省值就行了,除了 maxLifetime 和 maximumPoolSize 要注意自己计算一下。
其他的配置(sqlSessionFactory、MyBatis MapperScannerConfigurer、transactionManager 等)统统不用变。

其他关于 Datasource 配置参数的建议:
Configure your HikariCP idleTimeout and maxLifeTime settings to be one minute less than the wait_timeout of MySQL.
对于有 Java 连接池的系统,建议 MySQL 的 wait_timeout 使用缺省的 8 小时(http://www.rackspace.com/knowledge_center/article/how-to-change-the-mysql-timeout-on-a-server)。

另外:对于 web 项目,记得要配置:destroy-method="shutdown"