JohnShen's Blog.

[HikariCP] HikariCP为什么快

字数统计: 807阅读时长: 3 min
2019/10/04 Share

“Simplicity is prerequisite for reliability.”

HikariCP 的诞生

Hikari日语发音是Hi-ka-li,翻译成“光”,赋予两个含义:速度快,代码量少。作者 Brett Wooldridge 在工作时发现使用已有连接池时发现死锁问题,检查源码时发现存在大量的锁和嵌套。此外,Brett Wooldridge 研究的所有池都以多种方式违反了JDBC规约。当连接关闭或者返回,或者清除警告,或者回滚未提交的事务时,这些连接池并不会自动关闭语句Statements。并且它们不会重置用户更改的属性,如自动提交或事务隔离级别,以及更多的一些参数,从而导致下一个消费者获得将“脏”连接。“难道这就是 Java 20 年后生态系统中连接池的状态?”出于挫败感和必要性,Brett Wooldridge 创建了 HikariCP。目前,SpringBoot 2.x 已经官方宣布使用 HikariCP 作为 SpringBoot 默认的数据库连接池。

HikariCP 为什么快

HikariCP是一款快到极致的数据库连接池。在 HikariCP Github Wiki 详细了介绍HikariCP所做的优化,总结如下:

  • 优化并精简字节码、优化代码和拦截器。
  • 使用 FastList 替代 ArrayList。
  • 更好的并发集合类实现 ConcurrentBag。
  • 其他针对BoneCP缺陷的优化,比如对于耗时超过一个CPU时间片的方法调用的研究。

FastList

执行完数据库操作之后,往往需要依次关闭 ResultSet、Statement、Connection,而开发者经常只关闭了 Connection,而忘了关闭 ResultSet 和 Statement。为了解决这种问题,最好的办法是当关闭 Connection 时,能够自动关闭 Statement。为了达到这个目标,Connection 就需要跟踪创建的 Statement,因此可以将创建的 Statement 保存在 List 里,这样当关闭 Connection 的时候,就可以依次将 List 里所有 Statement 关闭。

HikariCP 觉得 ArrayList 存在不足,自己开发了一个 List 接口的精简实现 —— FastList

  • ArrayList 每次调用 get() 方法时都会进行rangeCheck,检查索引是否越界,FastList 的实现中去除了这一检查。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // In ArrayList
    public E get(int index) {
    rangeCheck(index);
    return elementData(index);
    }
    private void rangeCheck(int index) {//这里有rangeCheck
    if (index >= size)
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    // In FastList
    public T get(int index) {//这里的get方法取消了rangeCheck
    return elementData[index];
    }
  • 当 Statement 关闭或 Connection 关闭时需要将对应的 Statement 从 List 中移除。通常情况下,JDBC 在同一个 Connection 创建了多个 Statement 时,后打开的 Statement 会先关闭,这种情况从尾部开始扫描将表现更好,FastList 从尾部到头部执行移除扫描。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    // In ArrayList
    public boolean remove(Object o) {
    if (o == null) {
    for (int index = 0; index < size; index++) // 从头到尾遍历
    if (elementData[index] == null) {
    fastRemove(index);// 从头到尾移除
    return true;
    }
    } else {
    for (int index = 0; index < size; index++)
    if (o.equals(elementData[index])) {
    fastRemove(index);
    return true;
    }
    }
    return false;
    }

    // In FastList
    public boolean remove(Object element) {
    for (int index = size - 1; index >= 0; index--) { // 从尾部遍历
    if (element == elementData[index]) {
    final int numMoved = size - index - 1;
    if (numMoved > 0) {
    System.arraycopy(elementData, index + 1, elementData, index, numMoved);
    }
    elementData[--size] = null;
    return true;
    }
    }
    return false;
    }

所以整体来看,FastList 的优化点还是很简单的。

ConcurrentBag

CATALOG
  1. 1. HikariCP 的诞生
    1. 1.1. HikariCP 为什么快
    2. 1.2. FastList
    3. 1.3. ConcurrentBag