“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 的优化点还是很简单的。