[笔记]HikariCP浅析2-HikariCP与其他连接池对比
背景
Hikari现在已经是Spring的默认数据库连接池组件,之前的SpringBoot 1.*一直都是DBCP2。而其实常用数据库连接池有很多。比如有:C3P0、DBCP2、Tomcat、OracleUcp等。
在 @被纵养的懒猫 博客带你读《HikariCP数据库连接池实战》之二:数据库连接池江湖 提到了相当多的数据库连接池的对比,本篇博客就是该篇博客的总结性的笔记。
使用数据库连接池的原因
以访问MySQL为例,执行一个SQL语句的完整TCP流程共经历TCP 3次握手建立连接、MySQL 3次握手认证、SQL语句执行、MySQL关闭、TCP 4次挥手关闭连接这5个步骤。
这样的方式有以下的问题:
- 创建连接和关闭连接的过程比较耗时,并发时系统会变得很卡顿。
- 如果并发量很大,数据库连接的总数就会被消耗光,增加数据库的负载,新的数据库连接请求就会失败。
- 频繁地创建连接和关闭连接,会导致JVM临时对象较多,GC频繁。
为了提升通信效率连接池就孕育而生,优点如下: - 资源重用更佳。数据库连接得到复用,减少了大量创建和关闭连接带来的开销
- 系统响应更快。提前拥有的连接能减少请求新连接的时间。
- 连接管理更灵活。数据库连接池作为一款中间件能独立出来。
数据库池必须具备的功能
- 资源重用(数据库连接池的核心思想)
- 事务处理(对数据库的操作符合ALL-ALL-NOTHING原则)
- 异常处理(对JDBC访问的异常统一处理)
- 检测及容灾(面对一些网络、时间等问题的自愈)
- 外部配置(各种主流数据库连接池官方文档最核心的部分)
- 连接数控制(不同的系统对连接数有不同的需求)
- 监控(一些自身管理机制来监视连接的数量及使用情况等)
- 定时任务(如空闲检查、最小连接数控制)
- 缓存(如PSCache等避免对SQL重复解析)
- 连接池的建立和释放(基本的功能)
连接池分述
c3p0
c3p0是Swaldman主要开发并维护的一款开源的数据库连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展来增强传统的JDBC。
优点是稳定,适用范围广。当然有一些问题,如文章中提到的:[1]
c3p0官网上列出了其自知的缺点,主要有两方面。
1)连接和语句基于每个身份验证进行池化。因此,如果一个池支持的DataSource用于获取[ user = alice,password = secret1]和[ user = bob,password = secret2]的连接,则会有两个不同的池,而 DataSource可能在最坏的情况下管理maxPoolSize属性指定的连接数的两倍 。
这一事实是DataSource规范定义的自然结果(允许通过多个用户身份验证获取Connections),并且要求单个池中的所有Connections在功能上相同。这个“问题”不会改变或修复。这里需要你了解过程中发生了什么。
2)Statement pooling的开销太高 。对于未对PreparedStatements执行重要预处理的驱动程序,池化开销超过任何节省的开销。因此,默认情况下应关闭语句池。如果用户的驱动程序确实需要进行预处理PreparedStatements,特别是如果通过IPC与RDBMS进行预处理,则用户可通过打开语句池来看到性能的显著提升(通过将配置属性maxStatements或maxStatementsPerConnection 设置为大于零的值来执行此操作)。
这里,我再补充第3点,即“APPARENT DEADLOCK”的问题。c3p0在从连接池中获取和返回连接的时候,采用了异步的处理方式,使用一个线程池异步将返回关闭了(没有真正关闭)的连接放入连接池中。这是一个非常严重的Bug,也是一个著名的c3p0问题,即高并发时会出现严重的性能问题。原因是,调用了上文中c3p0获取的连接的close方法是异步的,异步就是将连接放入一个事件队列中等待内部进行处理,而不是立即放入数据库连接池中。c3p0中的AcquireTask(获取任务)就会大量占用内部线程池,导致没有足够的线程来将数据库连接池外使用完的连接放回池内。当然,用户可以自己写一个优先级最高的线程来单独、优先、迅速地归还连接。
c3p0的性能在众多连接池中属于比较低的,在BoneCP的官网上明确写到BoneCP比c3p0/DBCP连接池快了25倍。c3p0与DBCP的一个主要的区别就是,DBCP没有自动回收空闲连接的功能,而c3p0有自动回收空闲连接的功能。本书的主角HikariCP在这些方面就处理得非常精妙,除了它数据结构的定义以外,单独的HouseKeeper等都是经过精心打磨的。
DBCP
Apache Commons DBCP是Apache下独立的数据库连接池组件。且并非独立实现连接池功能的,它内部依赖于Commons中的另一个子项目Apache Commons Pool。
Tomcat Pool
Tomcat Pool简单高效,支持高并发环境与多核(CPU)系统等。
一些不遵守的JDBC规范的问题
- 默认不会重置连接状态(如自动提交、事务隔离级别等),用户必须手动配置名为ConnectionState的JDBCInterceptor。
- 在自动提交中,如果连接池配置了autocommit=false,就需要在自己的事务中执行连接有效性测试isValid(),否则使用者获取的连接有可能就在一个事务进行中;
- Tomcat不会在自己的事务中封装连接测试或initSQL
- Tomcat JDBC没有在Connection返回到池时或从池中取出前调用clearWarnings()方法清除SQL警告
- 默认情况下Tomcat JDBC不会跟踪Statements,已经打开的Statements不会自动关闭, 除非手动配置一个StatementFinalizer拦截。
BoneCP
BoneCP是一个快速、免费、开源的Java数据库连接池(即JDBC Pool)。优点很多:如文章中提到的:[1]
BoneCP的特点如下:
1)具有高可扩展性的快速连接池。
2)在connection状态改变时,可配置回调机制(钩式拦截器)。
3)通过分区(Partitioning)来提升性能。
4)允许用户直接访问connection或statement。
5)自动扩展pool容量。
6)支持statement caching。
7)支持异步地获取connection(通过返回一个Future实现)。
8)以异步的方式施放辅助线程(helper threads),来关闭connection和statement,以获得高性能。
9)在每个新获取的connection上,通过简单的机制,执行自定义的statement(即通过简单的SQL语句来测试connection是否有效,对应的配置属性为initSQL)。
10)支持运行时切换数据库,而不需要停止(shut down)应用。
11)能够自动回放(replay)任何失败的事务(如数据库或网络出现故障)。
12)支持JMX。
13)可以延迟初始化(lazy initialization)。
14)支持使用XML或property文件的配置方式。
15)支持idle connection timeouts和max connection age。
16)自动检验connection(是否活跃等)。
17)允许直接从数据库获取连接,而不通过Driver。
18)支持Datasouce和Hibernate。
19)支持通过debugging hooks来定位获取后未关闭的connection。
20)支持通过debugging来显示被关闭了两次的connection的堆栈轨迹(stack locations)。
21)支持自定义pool name。
22)代码整洁有序。
23)免费,开源,纯Java编写,具有完整的文档。
问题:
- 无法在getConnection()的时候配置数据库连接池来测试连接。
- 不会在Connection返回到池时或从池中取出之前通过Connection.clearWarnings()方法清除SQL警告。
- 默认情况下不会关闭废弃的、已经打开的statements。
- 不会在自己的事务中封装连接测试或initSQL。
Druid
一款“为监控而生的数据库连接池”,具有稳定、高效、可监控、可扩展的数据库连接池。
总结
对比
结言
数据库连接本身是很简单的一个问题,但是当我们在实际生产中遇到大量的高并发或者异常时就需要考虑数据库连接是否能够被优化,不会成为我们连接数据库获取数据的一个不匹配点。就是前人的实际生产经验的总结,才有了数据库连接池的出现。
而数据作为非常重要的组件,高效是非常重要的,因为她不能成为连接的问题点,本身的稳定性也非常重要。
参考
- 以上内容主要参考总结自 @被纵养的懒猫 博客带你读《HikariCP数据库连接池实战》之二:数据库连接池江湖