如何设计一款高性能的数据库连接工具?过来看这里,这是一款号称拥有“光”一样链接速度的工具,我们看它是怎样实现的(第三节:Hikari是如何实现的之ConcurrentBag——高性能的原理)

续接上集如何设计一款高性能的数据库连接工具?过来看这里,这是一款号称拥有“光”一样链接速度的工具,我们看它是怎样实现的(第二节:Hikari是如何实现的底层逻辑)-优快云博客

1.ConcurrentBag(连接池存储容器)

今天分享Hikari中的一个最核心的类,ConcurrentBag,它是实现了连接池高性能的一个最重要的原因,这个类用来存放最终的PoolEntry(原生链接的包装)类型的连接对象,提供了基本的增删查的功能,被HikariPool持有,上面那么多的操作,几乎都是在HikariPool中完成的,HikariPool用来管理实际的连接生产动作和回收动作,实际操作的是ConcurrentBag类。

2.官方的解释

这是一个专门的并发包,在连接池方面实现了优于LinkedBlockingQueue和LinkedTransferQueue的性能。它尽可能使用ThreadLocal存储来避免锁,但如果ThreadLocal列表中没有可用项,则会扫描公共集合。当借用线程没有自己的线程时,ThreadLocal列表中未使用的项目可能会被“窃取”。这是一个“无锁”实现,使用专门的AbstractQueuedLongSynchronizer来管理跨线程信令。请注意,从包中“借用”的项目实际上并没有从任何收集中删除,因此即使放弃引用,也不会发生垃圾收集。因此,必须注意“归还”借用的对象,否则将导致内存泄漏。只有“移除”方法才能从Bag里完全移除对象。

3.状态转换

IConcurrentBagEntity简单理解为Hikari对JdbcConnection的一层封装,有四种状态

public interface IConcurrentBagEntry
   {
      int STATE_NOT_IN_USE = 0;
      int STATE_IN_USE = 1;
      int STATE_REMOVED = -1;
      int STATE_RESERVED = -2;

      boolean compareAndSet(int expectState, int newState);
      void setState(int newState);
      int getState();
   }

从代码里面可以看到,连接池对象,会有4个状态转换分别是:NOT_IN_USE、IN_USE、REMOVED、RESERVED,他们之间的转换逻辑如下图:

4.ConcurrentBag的结构

ConcurrentBag的结构如下图,重点关注

  • sharedList和threadList,是两个关于PoolEntry(JdbcConnection的封装)的列表,用来实际存储连接池中维护的Jdbc连接
  • listener,是指向HikariPool的引用,当ConcurrentBag获取连接时发现用完了,则通过listener的回调接口请求HikariPool多扩充点Jdbc连接入池
  • handoffQueue,和listener相关,HikariPool扩充连接池后将新的连接通过该队列提供给正在等待的线程,正在getConnection的线程会在queue的另一端阻塞住,等待扩充连接后喂给它们


5.成员变量

   private final CopyOnWriteArrayList<T> sharedList;
   private final boolean weakThreadLocals;

   private final ThreadLocal<List<Object>> threadList;
   private final IBagStateListener listener;
   private final AtomicInteger waiters;
   private volatile boolean closed;

   private final SynchronousQueue<T> handoffQueue;

ConcurrentBag 整体就是无锁设计,有三个重要的成员变量:

  • ThreadLocal 缓存,加快本地连接获取速度
  • CopyOnWriteArrayList,写时拷贝List
  • SynchronousQueue,无存储的等待队列

6.执行逻辑

获取数据库连接基本流程如下:

  1. 当取连接的时候会先去 ThreadLocal 去找以前用过的连接,如果找到连接状态是可以使用的话拿直接返回。(ThreadLocal 是本地资源,每个线程都优先去自己本地去找,所以竞争也更少,需要遍历的连接也更少,所以速度就更快)
  2. 找不到再去 sharedList 这个共享的写时复制列表中查找可用连接。
  3. 如果再找不到,则通过 handoffQueue 等待可用的连接,如果超过一定时间则返回 null。

7.最后回顾一下

ConcurrentBag 的优化思路就是本地缓存有的去本地缓存找连接,找不到就去公共的 sharedList 去找,还找不到就等着。

通过将连接本地存储化来减少竞争,又根据连接池读多写少的特性用 CopyOnWriteArrayList 来实现 sharedList 。

当然还有像上面 borrow 和 requite 的一些细节也值得品味,追求极致速度就需要扣细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Scalzdp

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值