jdbcUrl: jdbc:mysql://{ip}:3306/{db}?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&autoReconnect=true
username: {username}
password: {password}
maxActive: 5
initialSize: 1
idleTimeout: 60000
connectionTimeout: 60000
validationTimeout: 3000
maxLifetime: 60000
cachePrepStmts: true
prepStmtCacheSize: 500
prepStmtCacheSqlLimit: 2048
useServerPrepStmts: true
useLocalSessionState: true
useLocalTransactionState: true
rewriteBatchedStatements: true
cacheResultSetMetadata: true
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
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的数据源配置:
<!--BoneCpDatasource-->
<beanid="dataSourceBoneCp"class="com.jolbox.bonecp.BoneCPDataSource"destroy-method="close">
<propertyname="driverClass"value="${db.driverClass}"/>
<propertyname="jdbcUrl"value="${db.url}"/>
<propertyname="username"value="${db.username}"/>
<propertyname="password"value="${db.password}"/>
<propertyname="idleConnectionTestPeriodInMinutes"value="2"/>
<propertyname="idleMaxAgeInMinutes"value="2"/>
<propertyname="maxConnectionsPerPartition"value="2"/>
<propertyname="minConnectionsPerPartition"value="0"/>
<propertyname="partitionCount"value="2"/>
<propertyname="acquireIncrement"value="1"/>
<propertyname="statementsCacheSize"value="100"/>
<propertyname="lazyInit"value="true"/>
<propertyname="maxConnectionAgeInSeconds"value="20"/>
<propertyname="defaultReadOnly"value="true"/>
</bean>
HiKariCP的数据源配置:
<!--HikariDatasource-->
<beanid="dataSourceHikari"class="com.zaxxer.hikari.HikariDataSource"destroy-method="shutdown">
<!--<propertyname="driverClassName"value="${db.driverClass}"/>--><!--无需指定,除非系统无法自动识别-->
<propertyname="jdbcUrl"value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"/>
<propertyname="username"value="${db.username}"/>
<propertyname="password"value="${db.password}"/>
<!--连接只读数据库时配置为true,保证安全-->
<propertyname="readOnly"value="false"/>
<!--等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException,缺省:30秒-->
<propertyname="connectionTimeout"value="30000"/>
<!--一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟-->
<propertyname="idleTimeout"value="600000"/>
<!--一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQLwait_timeout参数(showvariableslike'%timeout%')-->
<propertyname="maxLifetime"value="1800000"/>
<!--连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count*2)+effective_spindle_count)-->
<propertyname="maximumPoolSize"value="15"/>
</bean>
其中,很多配置都使用缺省值就行了,除了maxLifetime和maximumPoolSize要注意自己计算一下。
其他的配置(sqlSessionFactory、MyBatis MapperScannerConfigurer、transactionManager等)统统不用变。
其他关于Datasource配置参数的建议:
Configure your HikariCPidleTimeoutandmaxLifeTimesettings to be one minute less than thewait_timeoutof MySQL.
我们知道SpringBoot自己在“后台”帮我们配置了很多原本需要我们手动去的东西,至于这个“后台”是啥,就是Starter机制。我们先来看一看常用的Starter启动器,他们都是以spring-boot-starter开头明名的,至于spring-boot这个前缀则被Spring Boot官方保留。
至于Starter机制是如何实现上文说的“自动变速箱”功能,简单说就是通过条件注解来实现的,SpringBoot通过条件注解确定需要加载哪些组件,读取哪些配置,还有一些控制加载顺序的注解。这里我们先来看一些常用的条件注解和控制加载顺序的注解:
这里以一个简单的SpringBoot中的依赖配置文件做说明,只加入最基础的配置。
可以看到有如下几个依赖需要加入:
1)spring-boot-starter-parent,这个是所有SpringBoot工程都需要加入的依赖项,可以说这个是一个“根依赖”,必须要加入,注意这里指定的版本号也会约束其他依赖项,相当于一个最上层的版本配置,这里指定的就是我们常说的SpringBoot的版本。
2)spring-boot-starter-web,这个是Web工程的依赖项,所有的Web程序都要加。
3)spring-boot-configuration-processor,这个是ConfiguaritionProperties的配置,有了它我们的Spring项目中的Jar就会包含上很多meta-data,这些元数据会在开发的时候通过idea给我们很多输入的提示。
可以说这是SpringBoot项目中最重要的配置文件,这里看下关于他的几个核心部分,首先关于它的描述是这样的:
大意是说这个文件是一个提供了通过maven编译的应用的依赖和插件管理一个“父配置文件”。嗯,就理解是管理基础依赖和插件的就可以了。
看他的resource标签,是这样的:
这里指定了资源的导入和导出路径,什么意思?就是你的配置文件配在对应的目录下,框架都会去扫描到读取的,按上面的配置会读src/main/resources目录下的配置文件的内容。比如我们在这里放了如下的配置文件:
这样程序启动的会去读application.properties和application.yml文件里面的配置的。这里要说一下,上面的配置加载是有顺序的,先加yml,yaml ,后加properties里面的配置,这也决定了properties文文件配置的优先级是要高于yaml文件的,或者说相同参数的配置,properties里面的参数会覆盖yaml里面的。
最后看到最上面还有一个parent标签,是这样描述的,这里描述了spring-boot-starter-parent的父依赖配置spring-boot-dependencies:
同样的我们看一看spring-boot-dependencies的描述文件,看看里面的properties标签,是这样:
喔,好多似曾相识的面孔在这里都能见到,比如aspectj,caffeine, dom4j,hikaricp,junit,log4j2, mysql,tomcat等等一些,而且你也看到了,这里帮我们把版本控制也做好了,防止了原来在Spring中我们自己手动引入依赖的时候的经常会发生的版本冲突问题,所谓的约定优于配置在这里就是一个典型的体现场景。
这个配置是web应用的核心配置,有了他不用像Spring再像Spring一样去导SpringMVC相关的依赖了,这里看一下这个文件的内容。
同样的也有一个parent标签指定父依赖spring-boot-starters.
另外dependencies标签指定是一些依赖坐标的打包,包含json,tomcat等等,包含版本控制都做好了。
其中spring-boot-starter的依赖又包含了如下的dependencies,有spring-boot,自动配置,日志,注解,core等等的依赖:
以上可以看到SpringBoot基本上通过spring-boot-starter-parent和spring-boot-starter-web两个配置就把起步依赖需要导入的依赖项把我们需要的依赖给导完了,这也就是SpringBoot的starter机制在依赖配置层的最好体现,实现了所谓约定优于配置。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)