概括总结
使用hikari数据源之后,相对于第003版的druid数据源,从提交订单
这个复杂的操作上来说,性能提升了17.79%。而从获取数据库连接
这一简单的操作上来说,hikari比druid优秀几百倍。
004版本更新说明
pom.xml文件中引入了新的jar包:
<!-- HikariCP数据库连接工具 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>3.2.0</version>
</dependency>
HikariCP-3.2.0.jar
包的大小是141KB,而003版本中的druid-1.1.10.jar
包的大小是2693KB,甚至都不是一个数量级的。
这一版本写了两个测试类:Version004Test
和Version004Test2
。其中Version004Test
用于测试提交订单的耗时情况,Version004Test2
用于测试获取数据库连接的耗时情况。另外,由于hikari的性能好得超乎想像,毫秒
这个单位已经太粗糙了,于是把Version004Test2
中的时间单位改成了纳秒
。
测试结果
Version004Test
的测试结果:
002版本(自己写的数据源)耗时(ms) | 003版本(druid数据源)耗时(ms) | 004版本(hikari数据库源)耗时(ms) |
---|---|---|
1047 | 1259 | 1035 |
003版本相对于002版本性能下降了: (1259 - 1047) / 1047 * 100% = 20.24%
004版本相对于003版本性能提升了: (1259 - 1035) / 1259 * 100% = 17.79%
004版本相对于002版本性能提升了: (1047 - 1035) / 1047 * 100% = 1.14%
Version004Test2
的测试结果:
日志打印如下:
druid 获取一个数据库连接平均耗时:2392283 纳秒(获取连接后不关闭连接)
hikari 获取一个数据库连接平均耗时:3268791 纳秒(获取连接后不关闭连接)
druid 获取一个数据库连接平均耗时:1812491 纳秒(获取连接后不关闭连接)
hikari 获取一个数据库连接平均耗时:61716 纳秒(获取连接后不关闭连接)
druid 获取一个数据库连接平均耗时:1800770 纳秒(获取连接后关闭连接)
hikari 获取一个数据库连接平均耗时:2175 纳秒(获取连接后关闭连接)
druid 获取一个数据库连接平均耗时:1834128 纳秒(获取连接后关闭连接)
hikari 获取一个数据库连接平均耗时:2614 纳秒(获取连接后关闭连接)
可以总结为:如果获取连接后,不关闭连接,则druid和hikari在性能上是一个级别的。但是由于实际项目中,获取连接后都会关闭连接,这时候hikari的性能比druid优秀几百倍:1800770 / 2175 = 827.94 倍。
【备注】:不同的机器上的测试结果会不一样,以上测试结果仅供参考。
hikari为什么这么优秀?
HikariCP自己宣传的卖点是"zero-overhead",翻译成中文也就是"零开销"。但是要注意它是打了引号的,意思可以理解为:虽然不是真的零开销,但是跟其他的数据源工具比起来,可以认为是零开销了。这样的宣传我是可以接受的。
这可不像是druid那样明目张胆地吹牛皮说自己是Java语言中最好的数据库连接池,连引号都不打。这样的宣传我也是可以接受的,毕竟druid在github上的星星数量确实是比其他的数据源高很多。
话说回来,hikari为什么这么优秀呢?
官方的回答是这样的:“how we do it here”
这个回答我在测试之前点进去过,一看是英文版的,马上关闭了。后来看到了Version004Test2
的测试结果,又打开了那个页面,靠着有道词典一字一句地把文章看完了。总结起来有以下两点:
-
ArrayList的优化:当从ArrayList中移除某个对象时,会
从头到尾
执行扫描,但是我们知道,数据库相关的对象有很多都是需要关闭
的,关闭的顺序一般是与打开的顺序相反的,即最需要关闭的对象是保存在列表的尾部的,这意味着,如果能从尾到头
来扫描的话,性能就会有提升。于是ArrayList就被自定义的FastList取代了。 -
字节码层面的优化:对比一下以下两行代码的区别:
代码段1:(其中
PROXY_FACTORY
是类ProxyFactory
的一个静态实例对象)
PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
代码段2:(其中
ProxyFactory
是一个类)
ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
你能看出谁比谁的性能更高吗?反正我是看不出来。不过,虽然从java代码的层面看不出来,但是一旦编译成了字节码,区别就很明显了(第二种性能更好)。我在这里就不列举了,你可以去这里看看。
当然了,这两点只是样本例子,并不代表这个工具包中只做了这两点优化。特别是其中的第2点,能涵盖的范围非常广泛,真是让人佩服。
源码
004版本的github源码在这里,如果不知道怎样运行项目,请参考这里