HikariCP,日语的含义是“光”,号称目前最快的数据库连接池。
它的高性能来自两个方面:
- 利用 Javassist 在字节码层面优化代理对象的创建,提升代理对象的调用性能。
- 在数据结构上采用定制的
FastList
和ConcurrentBag
来提升性能。
本文主要关注这两种数据结构的实现。
1. FastList
我们用 JDBC 编程的时候,首先是获取 Connection
、创建 Statement
、执行查询得到 ResultSet
,执行完成后依次关闭:ResultSet
、Statement
、Connection
,特别是一个逻辑里创建了多个 PreparedStatement
时,一般用完就关闭。
为了防止用户忘了关闭 Statement
、ResultSet
,连接池需要跟踪创建的 Statement
、ResultSet
,在连接返回到连接池时关闭这两类资源。
public abstract class ProxyConnection implements Connection
{
// 跟踪本连接创建的语句
private final FastList<Statement> openStatements;
private synchronized <T extends Statement> T trackStatement(final T statement) {
openStatements.add(statement);
return statement;
}
// Statement 关闭时回调此方法
final synchronized void untrackStatement(final Statement statement) {
openStatements.remove(statement);
}
// 创建语句时加入跟踪列表
public Statement createStatement() throws SQLException {
return ProxyFactory.getProxyStatement(this, trackStatement(delegate.createStatement()));
}
// Connection.close 方法回调此方法关闭打开的语句
private synchronized void closeStatements() {
final int size = openStatements.size();
if (size > 0) {
for (int i = 0; i < size && delegate != ClosedConnection.CLOSED_CONNECTION; i++) {
// 利用 try-with-resources 机制进行关闭
try (Statement ignored = openStatements.get(i)) {
// automatic resource cleanup
} catch (SQLException e) {
LOGGER.warn("{} - Connection {} marked as broken because of an exception closing open statements during Connection.close()",
poolEntry.getPoolName(), delegate);
leakTask.cancel();
poolEntry.evict("(exception closing Statements during Connection.close())");
// 包装的代理对象不再持有底层的连接
delegate = ClosedConnection.CLOSED_CONNECTION;
}
}
openStatements.clear();
}
}
关闭的顺序跟创建的顺序是相反的,要关闭并移除的对象一般在列表的末尾。而 ArrayList
的移除对象是从列表头部开始的,在这种场景下不高效。FastList
的实现是逆序查找要删除对象;对于根据下标进行的操作,移除了对下标合法性的检查,由连接池来保证。