HikariCP 高性能原理:为什么它是最快的连接池?
你可能听说过:HikariCP 是目前最快的数据库连接池。
但你知道它为什么这么快吗?
这一节,我们深入 HikariCP 的源码,揭示它的高性能秘密。
HikariCP 性能数据
┌─────────────────────────────────────────────────────────────────┐
│ 连接池性能对比(官方数据) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 测试环境:Core i7-8700, 32GB RAM, SSD │
│ 测试方法:单线程获取归还连接 10,000 次 │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ HikariCP ████████████████████████████████ 0.003ms │ │
│ │ Druid ██████████████████████████ 0.008ms │ │
│ │ DBCP2 ████████████████ 0.014ms │ │
│ │ C3P0 ████████████ 0.025ms │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ HikariCP 比 Druid 快 2-3 倍,比 C3P0 快 8 倍! │
│ │
└─────────────────────────────────────────────────────────────────┘核心技术一:ConcurrentBag
传统连接池的问题
大多数连接池使用 BlockingQueue 或 synchronized List 来管理连接:
java
// 传统方式:BlockingQueue
public class OldPool {
private BlockingQueue<Connection> connections;
public Connection getConnection() throws Exception {
return connections.take(); // 阻塞等待
}
}问题:
BlockingQueue使用ReentrantLock,开销大- 锁竞争严重
- 性能瓶颈
ConcurrentBag 的设计
HikariCP 使用了 ConcurrentBag,一个专为连接池设计的并发集合:
┌─────────────────────────────────────────────────────────────────┐
│ ConcurrentBag 结构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ConcurrentBag │
│ ├── CopyOnWriteArrayList → 存储所有连接 │
│ ├── ThreadLocal → 线程本地缓存 │
│ ├── SynchronousQueue → 等待队列 │
│ └── AtomicInteger → 计数器 │
│ │
└─────────────────────────────────────────────────────────────────┘源码解析
java
public class ConcurrentBag<T> implements AutoCloseable {
// 所有连接的列表(CopyOnWriteArrayList,读取不加锁)
private final CopyOnWriteArrayList<IBagStateListener> listeners;
private final CopyOnWriteArrayList<T> sharedList;
// 线程本地缓存
private final ThreadLocal<ArrayList<T>> threadList;
// 等待队列(无缓冲,线程直接交换)
private final SynchronousQueue<T> handoffQueue;
/**
* 获取连接
*/
public E borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException {
// 1. 先从线程本地缓存获取
ArrayList<T> threadLocalList = threadList.get();
if (!threadLocalList.isEmpty()) {
return threadLocalList.remove(threadLocalList.size() - 1);
}
// 2. 从共享列表获取
for (T candidate : sharedList) {
if (candidate.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
if (listenerBag.borrow(candidate, timeout, timeUnit) == null) {
candidate.setState(STATE_NOT_IN_USE);
}
return candidate;
}
}
// 3. 尝试创建新连接
if (total < maximumPoolSize) {
E bagEntry = newEntry();
if (bagEntry != null) {
total++;
return bagEntry;
}
}
// 4. 等待其他线程释放
return handoffQueue.poll(timeout, timeUnit);
}
}性能优势
| 特性 | BlockingQueue | ConcurrentBag |
|---|---|---|
| 读取锁 | 加锁 | 无锁(COW) |
| 写入锁 | 加锁 | 无锁(CAS) |
| 线程本地 | 无 | 有 |
| 等待方式 | 阻塞 | 自旋 + CAS |
核心技术二:FastList
传统 List 的问题
大多数连接池使用 ArrayList 管理 Statement:
java
// 传统 ArrayList
ArrayList<Statement> statements = new ArrayList<>();
// add - 需要扩容检查
statements.add(stmt);
// remove - 需要移动元素
statements.remove(stmt); // O(n)FastList 的优化
HikariCP 使用自定义的 FastList:
java
/**
* FastList 是 ArrayList 的优化版本
* 特点:逆序添加,逆序移除,避免范围检查
*/
public class FastList<T> implements List<T> {
private Object[] elementData;
private int size;
@Override
public boolean add(T e) {
// 逆序添加 - 大多数情况下是关闭当前 Statement
if (size == elementData.length) {
elementData = Arrays.copyOf(elementData, size * 2);
}
elementData[size++] = e;
return true;
}
@Override
public T remove(int index) {
// 逆序移除 - 关闭 Statement 时通常关闭最后一个
if (index != --size) {
T e = (T) elementData[size];
elementData[size] = null; // 释放引用
return e;
}
T e = (T) elementData[size];
elementData[size] = null;
return e;
}
@Override
@SuppressWarnings("unchecked")
public T removeLast() {
// 快速移除最后一个元素 - O(1)
if (size > 0) {
size--;
T e = (T) elementData[size];
elementData[size] = null;
return e;
}
throw new IndexOutOfBoundsException();
}
}性能对比
┌─────────────────────────────────────────────────────────────────┐
│ FastList vs ArrayList │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 操作 │ ArrayList │ FastList │
│ ──────────────────────────────────────────────────────────── │
│ add │ O(1) │ O(1) │
│ remove(0) │ O(n) │ O(n) │
│ removeLast │ O(1) │ O(1) ⚡ │
│ remove(o) │ O(n) │ O(n) │
│ │
│ 关闭 Statement 时: │
│ - ArrayList: remove(stmt) 需要遍历找到位置 │
│ - FastList: removeLast() 直接移除最后一个 ⚡ │
│ │
└─────────────────────────────────────────────────────────────────┘核心技术三:字节码增强
Javassist 动态代理
HikariCP 使用 Javassist 生成动态代理:
java
// 生成连接代理类
PoolEntry poolEntry = new PoolEntry(connection, this);
final Object proxyConnection = ProxyFactory.getProxyBuilder(
connection,
(ps) -> {
// 关闭 PreparedStatement 时,直接移除
if (ps instanceof PreparedStatement) {
list.remove((PreparedStatement) ps);
}
}
).build();
// 生成 PreparedStatement 代理
PreparedStatement stmt = ((PreparedStatement) Proxy.newProxyInstance(
PreparedStatement.class.getClassLoader(),
new Class[]{ PreparedStatement.class },
(proxy, method, args) -> {
Object result = method.invoke(delegate, args);
if (CLOSE_METHOD.equals(method.getName())) {
// 关闭时,直接从 FastList 移除 - O(1)
list.remove((PreparedStatement) proxy);
}
return result;
}
));性能提升
- 避免反射调用
- 内联优化
- JIT 友好
核心技术四:最小化锁竞争
多种状态
HikariCP 使用多种连接状态:
java
// 连接状态枚举
public enum PoolBase {
STATE_NOT_IN_USE(0), // 未使用
STATE_IN_USE(1), // 使用中
STATE_REMOVED(2), // 已移除
STATE_RESERVED(3); // 预留(用于其他操作)
}CAS 操作
java
// 使用 CAS 设置状态
public boolean compareAndSet(int expectState, int newState) {
return UNSAFE.compareAndSwapInt(this, stateOffset, expectState, newState);
}
// 获取连接
public Connection getConnection() {
// 1. 尝试 CAS 获取
for (T bagEntry : sharedList) {
if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry.getConnection();
}
}
// 2. 等待或创建
return waiters();
}无锁化设计
┌─────────────────────────────────────────────────────────────────┐
│ 无锁化 vs 加锁 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 加锁方式 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ synchronized (lock) { │ │
│ │ if (available) return connection; │ │
│ │ wait(); │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ 问题:所有线程竞争同一把锁 │
│ │
│ 无锁方式 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ for (bagEntry : sharedList) { │ │
│ │ if (bagEntry.compareAndSet(NOT_IN_USE, IN_USE)) { │ │
│ │ return bagEntry.getConnection(); │ │
│ │ } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ 优势:多线程并行执行,无竞争 │
│ │
└─────────────────────────────────────────────────────────────────┘核心技术五:连接生命周期管理
快速初始化
java
// HikariCP 快速初始化
public HikariPool() {
// 1. 快速检查
checkFailFast();
// 2. 快速启动
poolName = "HikariPool-" + poolId.incrementAndGet();
// 3. 异步填充初始连接
fillPool();
}延迟创建
java
// 连接延迟创建
private Connection getConnection(long timeout) {
// 1. 先尝试从池获取
Connection connection = borrowEntry(timeout, SECONDS, connectionBag);
// 2. 验证连接
if (!isAutoCommit) {
connection.setAutoCommit(true);
}
return connection;
}快速销毁
java
// 快速关闭连接
public void closeConnection(Connection connection, String closureReason) {
// 直接标记为不可用
entry.compareAndSet(STATE_IN_USE, STATE_REMOVED);
// 快速移除
connectionBag.remove(entry);
}HikariCP 配置最佳实践
最小配置
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: root生产配置
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
hikari:
# 连接池名称
pool-name: HikariPool-Order
# 最小空闲连接数
minimum-idle: 10
# 最大连接数
maximum-pool-size: 50
# 连接最大生命周期
max-lifetime: 1800000 # 30 分钟
# 获取连接超时
connection-timeout: 30000 # 30 秒
# 空闲超时
idle-timeout: 600000 # 10 分钟
# 连接泄漏检测
leak-detection-threshold: 60000 # 60 秒
# 连接测试
connection-test-query: SELECT 1性能调优参数
yaml
spring:
datasource:
hikari:
# 性能优化参数
auto-commit: true
cachePrepStmts: true # 缓存 PreparedStatement
prepStmtCacheSize: 250 # PreparedStatement 缓存大小
prepStmtCacheSqlLimit: 2048 # SQL 缓存长度限制
useServerPrepStmts: true # 服务端预编译监控指标
通过 JMX 监控
java
// 获取 HikariCP MBean
HikariDataSource ds = (HikariDataSource) dataSource;
HikariPoolMXBean pool = ds.getHikariPoolMXBean();
// 获取指标
System.out.println("活跃连接: " + pool.getActiveConnections());
System.out.println("空闲连接: " + pool.getIdleConnections());
System.out.println("总连接数: " + pool.getTotalConnections());
System.out.println("等待线程: " + pool.getThreadsAwaitingConnection());
System.out.println("最大连接: " + pool.getMaximumPoolSize());通过 Micrometer 监控
java
// Spring Boot Actuator + Micrometer 自动暴露 HikariCP 指标
@Bean
public MeterRegistry meterRegistry() {
return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
}
// 访问 /actuator/prometheus 查看 hikaricp 指标面试高频问题
Q1:HikariCP 为什么比其他连接池快?
- ConcurrentBag:无锁并发集合,减少锁竞争
- FastList:逆序移除 Statement,避免遍历
- Javassist:字节码增强,减少反射
- CAS 操作:替代 synchronized
- ThreadLocal:线程本地缓存
Q2:ConcurrentBag 的工作原理?
使用 CopyOnWriteArrayList 存储连接,ThreadLocal 缓存,SynchronousQueue 等待,实现无锁化并发。
Q3:FastList 相比 ArrayList 优化了什么?
逆序移除最后一个元素时,FastList 是 O(1),ArrayList 也是 O(1)。但 FastList 移除了边界检查和一些不必要的操作。
最佳实践
- 使用 HikariCP:性能最优,Spring Boot 2.x 默认
- 合理配置连接数:CPU 核心数 * 2
- 监控连接池:接入 Micrometer 或 JMX
- 配置连接泄漏检测:设置
leak-detection-threshold - 使用 PreparedStatement:HikariCP 会缓存
思考题
HikariCP 的设计理念是「最小化一切开销」。
你能想到还有哪些地方可以优化吗?
提示:网络通信、内存分配、异常处理等。
