Hikari连接池目前公认是性能最高的数据库连接池,同时也是SpringBoot2.0以后默认使用的数据库连接池。
由于Springboot2.0默认就是使用的Hikari连接池,所以无需额外添加Hikari相关的maven依赖。只需要在application.yml添加对应的配置即可,如下:
1、Hikari中的核心类为HikariDataSource,表示Hikari连接池中的数据源,实现了DataSource接口的getConnection方法,getConnection方法源码如下:
getConnection方法逻辑不多,主要是调用了HikariPool的getConnection()方法,而HikariDataSource中有两个HikariPool对象,一个是fastPathPool是在HikariPool有参构造函数中创建, 如果没有创建fastPathPool,那么就会在getConnection方法时创建pool对象。
很显然pool对象是由volatile关键字修饰的,而fastPathPool是final类型的,所以fastPathPool的效率会比pool要高,所以推荐使用HikariDataSource有参构造函数进行初始化。
2、由上可知获取连接的逻辑是在HikariPool的getConnection方法中,继续分析HikariPool的getConnection方法,源码如下:
核心步骤只有两步,一个是调用ConcurrentBag的borrow方法借用一个PoolEntry对象,第二步调用调用PoolEntry的createProxyConnection方法动态生成代理connection对象。
这里涉及到了两个核心的类,分别是ConcurrentBag和PoolEntry
3、PoolEntry
PoolEntry顾名思义是连接池的节点,实际也可以看作是一个Connection对象的封装,连接池中存储的连接就是以PoolEntry的方式进行存储。
PoolEntry内部属性如下:
4、ConcurrentBag
ConcurrentBag直意就是并发包,本质就是连接池的主体,存储连接的封装对象PoolEntry,另外做了并发控制来解决连接池的并发问题。
ConcurrentBag的内部属性如下:
5、从ConcurrentBag借出一个元素
ConcurrentBag实现了borrow方法,意思是从并发集合中借出一个元素,对于连接池而言实际就是从连接池中获取一个连接,源码如下:
从源码中可以发现方法中共有三个地方出现了return bagEntry,所以可以看出ConcurrentBag借出元素的地方是有三个来源的
第一步:从ThreadLocal中获取
每个线程从ConcurrentBag中借出连接时都会创建一个ThreadLocal对象,值为一个List,默认大小为16,每次客户端从ConcurrentBag中获取连接时都会优先从ThreadLocal中尝试获取连接,获取失败才会走下一步获取。
当客户端将连接归还给ConcurrentBag时,首先判断当前是否有其他客户端等待连接,如果有其他客户端等待那么就将连接给其他客户端,如果没有客户端等待那么将连接存入ThreadLocal中,每个ThreadLocal最多会存储50个连接
Tips:使用ThreadLocal可能会存在内存泄露的风险,所以ConcurrentBag内部有一个属性为booleal weakThreadLocals,当值为true时则ThreadLocal中的引用均是弱引用,在内存不足时GC的时候会被回收,避免了出现内存泄露的问题。
第二步:从sharedList中获取
从ThreadLocal中获取连接失败之后,会再次尝试从sharedList中获取,sharedList集合存在初始化的PoolEntry。在ConcurrentBag初始化的,会初始化指定数量的PoolEntry对象存入sharedList,源码如下:
ConcurrentBag构造函数如下:
HikariPool内部属性包含了ConcurrentBag对象,在HikariPool初始化时会创建ConcurrentBag对象,所以ConcurrentBag的构造函数是在HikariPool初始化时调用,HikariPool构造函数如下:
这里有一个定时任务houseKeeperTask,该定时任务的作用是定时检测连接池中连接的数量,执行的内容就是HouseKeep的run方法,逻辑如下:
该定时任务主要是为了维护连接池中连接的数量,首先需要将被标记为需要丢弃的连接进行关闭,然后将空闲超时的连接进行关闭,最后当连接池中的连接少于最小值时就需要对连接池进行补充连接的操作。所以在初始化连接池时,初始化连接的操作就是在fillPool方法中实现的。fillPool方法源码如下:
先计算需要创建的连接数量,向创建连接的线程池中提交任务 poolEntryCreator,创建最后一个任务时创建的是postFillPoolEntryCreator, 两者没有本质的区别,只是打印的日志不一样而已.
PoolEntryCreator创建PoolEntry对象的逻辑如下:
createPoolEntry方法逻辑如下:
首先创建一个新的PoolEntry对象,PoolEntry构造时会创建Connection对象,另外如果连接设置了最大生命周期时长,那么需要给每个PoolEntry添加定时任务,为了防止多个PoolEntry同时创建同时被关闭,所以每个PoolEntry的最大生命周期时间都不一样。当PoolEntry达到最大生命周期后会触发softEvictConnection方法,将PoolEntry标记为需要被丢弃,另外由于抛弃了PoolEntry对象,所以需要重新调用addBagItem方法对PoolEntry对象进行补充。
第三步:通过IBagStateListener创建新的元素
由于第二步可知,IBagStateListener主要有一个addBagItem方法,HikariPool实现了addBagItem方法,方法源码如下:
总结:
从ConcurrentBag中获取连接一共分成三步,首先从当前线程的ThreadLocal中获取,如果有直接返回一个连接,如果ThreadLocal中没有则从sharedList中获取,sharedList可以理解为ConcurrentBag缓存的连接池,每当创建了一个PoolEntry对象之后都会添加到sharedList中去,如果sharedList中的连接状态都不是可用状态,此时就需要通过IBagStateListener提交一个创建连接的任务,交给创建连接的线程池去执行,创建新的连接。
新的连接创建成功之后会将PoolEntry对象添加到无容量的阻塞队列handoffQueue中,等待连接的线程不断尝试从handoffQueue队列中获取连接直到成功获取或者超时返回。
当客户端释放连接时会调用collection的close方法,Hikari中的Connection使用的是代理连接ProxyConnection对象,调用close方法时会调用关联的PoolEntry对象的回收方法recycle方法,PoolEntry的recycle方法源码如下:
回收连接最终会调用ConcurrentBag的requite方法,方法逻辑不复杂,首先将PoolEntry元素状态设置为未使用,然后判断当前是否存在等待连接的线程,如果存在则将连接加入到无界阻塞队列中去,由等待连接的线程从阻塞队列中去获取;
如果当前没有等待连接的线程,则将连接添加到本地线程变量ThreadLocal中,等待当前线程下次获取连接时直接从ThreadLocal中获取。
1、采用自定义的FastList替代了ArrayList,FastList的get方法去除了范围检查rangeCheck逻辑,并且remove方法是从尾部开始扫描的,而并不是从头部开始扫描的。因为Connection的打开和关闭顺序通常是相反的
2、初始化时创建了两个HikariPool对象,一个采用final类型定义,避免在获取连接时才初始化,因为获取连接时才初始化就需要做同步处理
3、Hikari创建连接是通过javassist动态字节码生成技术创建的,性能更好
4、从连接池中获取连接时对于同一个线程在threadLocal中添加了缓存,同一线程获取连接时没有并发操作
5、Hikari最大的特点是在高并发的情况下尽量的减少锁竞争
到此这篇达梦数据库连接池如何配置(达梦查看数据库连接)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/sjkxydsj/62779.html