Skip to content

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

传统连接池的问题

大多数连接池使用 BlockingQueuesynchronized 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);
    }
}

性能优势

特性BlockingQueueConcurrentBag
读取锁加锁无锁(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 为什么比其他连接池快?

  1. ConcurrentBag:无锁并发集合,减少锁竞争
  2. FastList:逆序移除 Statement,避免遍历
  3. Javassist:字节码增强,减少反射
  4. CAS 操作:替代 synchronized
  5. ThreadLocal:线程本地缓存

Q2:ConcurrentBag 的工作原理?

使用 CopyOnWriteArrayList 存储连接,ThreadLocal 缓存,SynchronousQueue 等待,实现无锁化并发。

Q3:FastList 相比 ArrayList 优化了什么?

逆序移除最后一个元素时,FastList 是 O(1),ArrayList 也是 O(1)。但 FastList 移除了边界检查和一些不必要的操作。


最佳实践

  1. 使用 HikariCP:性能最优,Spring Boot 2.x 默认
  2. 合理配置连接数:CPU 核心数 * 2
  3. 监控连接池:接入 Micrometer 或 JMX
  4. 配置连接泄漏检测:设置 leak-detection-threshold
  5. 使用 PreparedStatement:HikariCP 会缓存

思考题

HikariCP 的设计理念是「最小化一切开销」。

你能想到还有哪些地方可以优化吗?

提示:网络通信、内存分配、异常处理等。

基于 VitePress 构建