Hikari并发无锁化详解

2 篇文章 0 订阅

前言

  • 目前SpringBoot默认的数据库连接池是Hikari,相对于Druid连接池来说:
  • Hikari的特点就是快,其内部运用了很多优化机制和操作,主要就是为了更高的性能。
  • 而Druid连接池特点不是快,是对数据和sql的监控分析。
  • 两者各有各的特点,没有哪个是最好,根据项目和需求选择适合的连接池。

FastList

  • Hikari连接池内部为了有更好的性能,放弃了使用ArrayList集合,而是自定义了一个FastList集合
  • 其内部和ArrayList实现相似,主要在get方法和remove方法有了不同的调整优化

1.png

  • 上图里圈起来的部分是FastList对获取元素方法的优化,去除了对下标索引的验证。

2.png

  • 上图中指出的位置是FastList对删除元素方法的优化,ArrayList是从头开始扫描元素进行删除,FastList是从尾部开始扫描元素进行删除
  • 在Hikari里一般是会删除(关闭)最近一个放入的元素,所以FastList会避免一部分的无效查找。

ConcurrentBag

  • Hikari连接池内部除了自定义了FastList集合外,还自定义了ConcurrentBag并发访问集合
  • ConcurrentBag是可以多线程并发访问,但看其内部源码会发现没有锁定资源操作
  • 支持并发操作主要是其内部的三个属性:threadList、sharedList、handoffQueue

3.png

  • threadList:保存当前线程的本地链接资源
  • sharedList:保存所有的链接资源
  • handoffQueue:阻塞式一进一出队列
/**
 * The method will borrow a BagEntry from the bag, blocking for the
 * specified timeout if none are available.
 *
 * @param timeout how long to wait before giving up, in units of unit
 * @param timeUnit a <code>TimeUnit</code> determining how to interpret the timeout parameter
 * @return a borrowed instance from the bag or null if a timeout occurs
 * @throws InterruptedException if interrupted while waiting
 */
public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
{
   // Try the thread-local list first
   final List<Object> list = threadList.get();
   for (int i = list.size() - 1; i >= 0; i--) {
      final Object entry = list.remove(i);
      @SuppressWarnings("unchecked")
      final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
      if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
         return bagEntry;
      }
   }

   // Otherwise, scan the shared list ... then poll the handoff queue
   final int waiting = waiters.incrementAndGet();
   try {
      for (T bagEntry : sharedList) {
         if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
            // If we may have stolen another waiter's connection, request another bag add.
            if (waiting > 1) {
               listener.addBagItem(waiting - 1);
            }
            return bagEntry;
         }
      }

      listener.addBagItem(waiting);

      timeout = timeUnit.toNanos(timeout);
      do {
         final long start = currentTime();
         final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
         if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
            return bagEntry;
         }

         timeout -= elapsedNanos(start);
      } while (timeout > 10_000);

      return null;
   }
   finally {
      waiters.decrementAndGet();
   }
}
  • borrow()获取链接资源方法执行流程:
  • 首先在threadList对象获取当前线程保存的链接资源,若获取成功,就设置状态为STATE_IN_USE并返回链接资源;
  • threadList里获取链接资源失败,就在sharedList集合里获取空闲连接资源,若获取成功,就设置状态为STATE_IN_USE并返回链接资源;
  • sharedList里获取链接资源失败,就在handoffQueue队列里等空闲连接资源进入,超时返回;
注:sharedList返回的链接资源后,其还是存在sharedList集合内部,其他线程也能访问这个链接资源,只是其状态为STATE_IN_USE,不能被使用。
注:sharedList对象是使用CopyOnWriteArrayList集合,符合当前Hikari连接池里是读多写少场景(链接资源数固定,并复用资源)。
/**
 * This method will return a borrowed object to the bag.  Objects
 * that are borrowed from the bag but never "requited" will result
 * in a memory leak.
 *
 * @param bagEntry the value to return to the bag
 * @throws NullPointerException if value is null
 * @throws IllegalStateException if the bagEntry was not borrowed from the bag
 */
public void requite(final T bagEntry)
{
   bagEntry.setState(STATE_NOT_IN_USE);

   for (int i = 0; waiters.get() > 0; i++) {
      if (bagEntry.getState() != STATE_NOT_IN_USE || handoffQueue.offer(bagEntry)) {
         return;
      }
      else if ((i & 0xff) == 0xff) {
         parkNanos(MICROSECONDS.toNanos(10));
      }
      else {
         yield();
      }
   }

   final List<Object> threadLocalList = threadList.get();
   threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
}
  • requite()放回链接资源方法执行流程:
  • 首先把当前链接资源对象的状态设置为STATE_NOT_IN_USE,
  • 然后判断当前链接资源是否已经被其他线程使用,若没有再把当前链接资源放入队列唤醒阻塞的线程,循环256次后睡眠10纳秒;
  • 最后把链接资源对象放入本地线程资源内;

最后

  • Hikari连接池使用ConcurrentBag集合并发无锁化机制和FastList集合提高性能
  • 当然在Hikari里的快不只是因为这两个集合,还有很多其他的优化到极致的点
  • 参考:这波性能优化,太炸裂了!
  • 虚心学习,共同进步 -_-
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值