数据库连接池原理
在系统初始化的时候,在内存中开辟一片空间,将一定数量的数据库连接作为对象存储在对象池里,并对外提供数据库连接的获取和归还方法。用户访问数据库,并不是建立一个新的连接,而是从数据库连接池中取出一个已有的空闲连接对象;使用完毕归还后的连接也不会马上关闭,而是由数据库连接池统一管理回收,为下一次借用做好准备。如果由于高并发请求导致数据库连接池的连接被借用完毕,其它线程就会等待,直到有连接被归还。整个过程中,连接并不会关闭,而是源源不断地循环使用,有借有还。
常见数据库连接池
C3P0:实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象
DBCP: Apache下独立的数据库连接池组件,由于Apache的缘故,它可能是使用最多的开源数据库连接池
BoneCP: 在c3p0和DBCP存在的时代,BoneCP的出现就是为了追求极致,并且提供了完善的基准测试
Druid: 阿里出品,是阿里巴巴唯一使用的数据库连接池,阿里云DRDS和阿里TDDL都采用了Druid,可支持”双十一”等最严苛的使用场景,并且提供了强大的监控功能,在国内有不少用户。
HikariCP: HiKariCP是数据库连接池的一个后起之秀,号称性能最好,可以完美地PK掉其他连接池,Springboot 2.0选择HikariCP作为默认数据库连接池
有一个争论是HikariCP与Druid相比哪个更好,对此Druid作者温少是直接上场对过线的,感兴趣的可以参考:
https://github.com/brettwooldridge/HikariCP/issues/232
HikariCP为什么这么快
在HikariCP官网(https://github.com/brettwooldridge/HikariCP/wiki/Down-the-Rabbit-Hole)详细介绍了HikariCP所做的优化:
- 优化并精简字节码、优化代码和拦截器
- 使用FastList替代ArrayList
- 有更好的并发集合类实现ConcurrentBag
- 其它针对BoneCP缺陷的优化,比如对耗时超过一个CPU时间片的方法调用的研究
接下来将探究FastList和ConcurrentBag的实现
FastList
HikariCP重现设计了一个List接口实现类,用以替换ArrayList。FastList是List接口的精简实现,只实现了接口中必要的几个方法。
jdk中的ArrayList:
1 | public class ArrayList<E> extends AbstractList<E> |
HikariCP中的FastList:
1 | public final class FastList<T> implements List<T>, RandomAccess, Serializable |
可以看到FastList并没有继承AbstractList
ArrayList的get方法:
1 | public E get(int index) { |
FastList的get方法:
1 | public T get(int index) { |
可以看出FastList的get方法取消了rangeCheck,在一定程度上追求了极致。
ArrayList的remove(Object)方法:
1 | public boolean remove(Object o) { |
与ArrayList相反,FastList选择从数组的尾部开始遍历(JDBC编程中的常见模式是在使用后立即关闭Statement,或者以打开的相反顺序关闭Statement,可以理解为同一个Connection创建了多个Statement时,后打开的Statement会先关闭),因而更加高效。
1 | public boolean remove(Object element) { |
ConcurrentBag
参考资料
HikariCP数据库连接池实战