首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Durid 和 HikariCP,哪个更好?

Durid 和 HikariCP,哪个更好?

作者头像
苏三说技术
发布2026-05-21 20:34:10
发布2026-05-21 20:34:10
1230
举报
文章被收录于专栏:苏三说技术苏三说技术

前言

最近有球友问了我一个问题:Druid和HikariCP,到底哪个更好?

你在做技术选型的时候,是不是经常在两个优秀的开源组件之间纠结?

Druid和HikariCP就是典型的例子。

一个是阿里巴巴开源的“全能型选手”,一个是Spring Boot默认集成的“性能怪兽”。

很多小伙伴在工作中肯定遇到过类似的问题:项目到底该用哪一个?

网上众说纷纭,有人说HikariCP快,有人说Druid功能强。

今天这篇文章就专门跟大家一起聊聊这个话题,希望对你会有所帮助。

一、先来聊聊数据库连接池是干啥的

要搞清楚哪个更好,我们得先弄明白连接池到底在解决什么问题。

说来惭愧,刚入行那会儿,我写的数据库代码是这样的:

代码语言:javascript
复制
// 错误示例:每次请求都创建新连接
public void getData() {
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection(url, user, password);
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT * FROM users");
    // ...
    rs.close();
    stmt.close();
    conn.close();
}

这样写有什么问题?

大家可以想象一下:一个高并发的系统,每秒有成百上千个请求访问数据库,每个请求都要建立一个数据库连接——三次握手、账号验证、建立Session,用完立马关掉。

数据库的CPU光伺候这些连接的建立和销毁就要累死了。

连接池本质上就是个“资源池”:在程序启动的时候预先创建一批数据库连接,存在池子里。

请求来了,从池子里“借”一个连接;请求处理完了,把连接“还”回去,而不是关闭。

这样就省去了反复创建和销毁连接的开销。原理其实很简单,就像下面这张图画的:

目前Java界用得最广的两个连接池就是HikariCP和Druid。

Spring Boot 2.x/3.x默认集成了HikariCP,而Druid是阿里巴巴出品的老牌劲旅。

二、HikariCP到底有多快?

2.1 一个简单的性能测试

我们先通过一个简单的测试来看HikariCP到底有多快。

在100并发下,连接获取延迟的差距就很明显了:

代码语言:javascript
复制
// 模拟高并发获取连接
publicclass ConnectionPoolBenchmark {
    
    @Test
    public void testHikari() throws Exception {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        config.setUsername("root");
        config.setPassword("123456");
        config.setMaximumPoolSize(20);
        // 开启泄漏检测(后面会讲到)
        config.setLeakDetectionThreshold(5000);
        
        HikariDataSource ds = new HikariDataSource(config);
        
        // 100个线程并发获取连接
        ExecutorService executor = Executors.newFixedThreadPool(100);
        long start = System.currentTimeMillis();
        
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                try (Connection conn = ds.getConnection()) {
                    // 模拟业务操作
                    conn.isValid(1);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        long end = System.currentTimeMillis();
        System.out.println("总耗时: " + (end - start) + "ms");
    }
}

实测数据显示:HikariCP在高并发下的表现确实非常抢眼,连接获取延迟极低,内存占用控制得也很好。

在TechEmpower基准测试中,HikariCP能支撑15万+的TPS,而Druid大约在8万-12万之间。

连接获取延迟方面,HikariCP通常小于5ms,Druid在10-25ms之间。

内存占用差异也很大:HikariCP核心代码只有约130KB,而Druid的功能模块比较多,大约2MB左右。

2.2 揭开HikariCP性能的秘密

很多小伙伴可能会问:凭什么HikariCP这么快?它到底用了什么黑科技?

我花了几个晚上读它的源码,总结出最核心的两点。

第一点:ConcurrentBag——无锁化的设计

HikariCP的核心是一个叫ConcurrentBag的类,这是它管理连接池的最重要的核心类,也是它性能甩开其他连接池十条街的秘密所在。

ConcurrentBag是一个lock-free的并发集合,依赖了CopyOnWriteArrayList、ThreadLocal、AtomicInteger等JDK的并发类实现。

咱们来看看它的核心逻辑:

代码语言:javascript
复制
public class ConcurrentBag<T> {
    // sharedList保存了所有的连接
    privatefinal CopyOnWriteArrayList<T> sharedList;
    // threadList保存当前线程用过连接的引用
    privatefinal ThreadLocal<List<Object>> threadList;
    // 等待获取连接的线程数
    privatefinal AtomicInteger waiters;
    
    /**
     * 从连接池中获取连接的核心方法
     */
    public T borrow(long timeout, final TimeUnit timeUnit) 
            throws InterruptedException {
        
        // ① 先尝试从ThreadLocal中拿——这一步完全无锁!
        List<Object> list = threadList.get();
        for (int i = list.size() - 1; i >= 0; i--) {
            final T bagEntry = (T) list.remove(i);
            // CAS操作,无锁更新连接状态
            if (bagEntry != null && 
                bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
                return bagEntry;
            }
        }
        
        // ② ThreadLocal中没有,再从sharedList中拿
        // 等待timeout时间,通过synchronizer进行线程间同步
        // ...
    }
}

这里的巧妙之处在于:先用ThreadLocal缓存当前线程使用过的连接,还回去的连接也会放到线程本地的ThreadLocal中。

这样一来,大部分情况下获取连接根本就不需要加锁,性能自然就上去了。

而Druid在获取连接、归还连接时都有锁控制,因为连接池资源是多线程共享的,不可避免会有锁竞争。

第二点:字节码精简优化

HikariCP在编译期进行了大量的字节码优化,比如用自定义的FastList替代ArrayList,减少了各种边界检查的开销。

这些优化细节加起来,才有了“性能怪兽”的美誉。

2.3 连接池大小的黄金法则

很多新手设置连接池的时候有个误区:觉得maximumPoolSize越大越好,一上来就设成1000。

但是告诉大家,连接池真不是越大越好

大家可以想一下:数据库服务器的CPU是有限的,假设只有4核,那同一时刻它真的只能做4件事。

给你1000个连接,996个都在排队,而且CPU还要花费大量时间在这些线程之间切来切去,得不偿失。

PostgreSQL官方和HikariCP的作者给出了一个广受认可的黄金公式:

最优连接数 = (CPU核心数 × 2) + 有效磁盘数

按照这个公式,4核CPU的数据库服务器,最优连接数只有9个!即使是生产环境,10-20个连接往往就能支撑几千并发了。

这听起来有点反直觉,但道理其实很简单——瓶颈通常在于磁盘I/O,而不是连接数不够。

另外还需要注意一个非常重要的配置:minimumIdle应该和maximumPoolSize保持一致。

否则连接池会随着流量波动不断扩容和缩容,反而造成了不必要的开销。

HikariCP的几个核心参数,大家可以记一下:

代码语言:javascript
复制
# HikariCP推荐配置(生产环境)
spring:
datasource:
    hikari:
      maximum-pool-size:20          # 最大连接数,根据黄金公式设置
      minimum-idle:20               # 建议等于maximumPoolSize,固定大小
      connection-timeout:3000       # 连接超时,建议调小到3秒
      idle-timeout:600000           # 空闲超时10分钟
      max-lifetime:1800000          # 最大存活30分钟,必须小于数据库wait_timeout
      leak-detection-threshold:60000# 连接泄漏检测,60秒未归还就报警
      validation-timeout:5000       # 连接校验超时5秒
  • max-lifetime必须小于数据库服务端的wait_timeout,不然拿到的是已经断开的连接,会报“Pipe broken”错误。
  • leak-detection-threshold这个开关非常重要,设置后如果连接超过阈值未被归还,会在日志中精准输出堆栈快照,直接定位到getConnection()的调用位置。

三、Druid凭什么还能占据半壁江山?

说完HikariCP,咱们再来看看Druid。既然HikariCP性能这么好,为什么还有那么多大厂在用Druid?答案很简单:Druid是“为监控而生”的连接池

3.1 Druid最核心的优势——监控能力

很多小伙伴在工作中应该遇到过这样的场景:线上系统突然变慢了,大量请求超时。

排查了一圈,发现是数据库慢查询导致连接被占满。

但问题是,慢查询的SQL到底是谁写的?哪段代码调用了它?如果没有SQL监控,抓起来非常痛苦。

Druid内置了完善的监控系统,可以实时监控连接池的状态(活跃连接数、空闲数等)、SQL执行情况(执行时间、行数、慢SQL等),甚至还能关联Web请求与数据库的交互关系。

下面这个是Druid监控体系的结构图:

开启Druid的监控非常简单,几步就能搞定:

代码语言:javascript
复制
# application.yml
spring:
datasource:
    druid:
      # 监控页面配置
      stat-view-servlet:
        enabled:true
        url-pattern:/druid/*
        login-username:admin
        login-password:123456
        reset-enable:false
      
      # Web监控过滤器
      web-stat-filter:
        enabled:true
        url-pattern:/*
        exclusions:"*.js,*.css,*.jpg,/druid/*"
        session-stat-enable:true
      
      # SQL监控配置
      filter:
        stat:
          enabled:true
          db-type:mysql
          log-slow-sql:true
          slow-sql-millis:1000# 超过1秒算慢查询
        wall:
          enabled:true           # 开启防火墙
          config:
            delete-allow:false   # 禁止全表删除
            drop-table-allow:false# 禁止删表

配置完成之后,访问 http://localhost:8080/druid 就能看到一个非常直观的监控页面,包含SQL监控、URL监控、Session监控等丰富的功能模块。

3.2 Druid的“杀手锏”

连接泄漏检测和生产环境故障排查。

Druid在连接泄漏检测方面做得非常到位。

电商大促、秒杀活动的时候,最容易出现的一个问题就是连接被拿走了却不归还,最终导致连接池耗尽。

Druid提供了三级防护体系:

代码语言:javascript
复制
@Configuration
publicclass DruidConfig {
    
    @Bean
    public DataSource druidDataSource() {
        DruidDataSource ds = new DruidDataSource();
        
        // 连接池基础配置
        ds.setUrl("jdbc:mysql://localhost:3306/test");
        ds.setUsername("root");
        ds.setPassword("123456");
        ds.setInitialSize(10);
        ds.setMaxActive(100);
        ds.setMinIdle(10);
        
        // 🔥 核心防泄漏配置
        ds.setRemoveAbandoned(true);              // 开启泄漏连接自动回收
        ds.setRemoveAbandonedTimeout(180);        // 180秒未归还算泄漏
        ds.setLogAbandoned(true);                 // 记录泄漏连接的堆栈信息
        ds.setAbandonWhenOverflow(true);          // 连接池满时立即回收泄漏连接
        
        // 连接保活配置
        ds.setValidationQuery("SELECT 1");
        ds.setTestWhileIdle(true);
        ds.setTimeBetweenEvictionRunsMillis(60000);
        
        // 开启监控和防火墙
        ds.setFilters("stat,wall");
        
        return ds;
    }
}

这些参数配置之后,一旦发生连接泄漏,Druid会自动回收连接,并在日志中打印出泄漏连接的堆栈,直接帮你定位到是哪一行代码没关连接!

这一点在生产环境中简直是救命稻草。

3.3 SQL防火墙——防注入的“守门员”

Druid内置了WallFilter,可以对SQL进行安全检查,拦截危险的SQL语句,比如DROP TABLEDELETE without WHERE等。

这在金融、医疗等合规要求比较高的场景下非常重要。

代码语言:javascript
复制
// 开启WallFilter配置
ds.setFilters("stat,wall");  // stat=监控,wall=防火墙

// 高级配置:自定义拦截规则
Map<String, String> wallConfig = new HashMap<>();
wallConfig.put("deleteAllow", "false");     // 禁止DELETE操作
wallConfig.put("dropTableAllow", "false");  // 禁止DROP TABLE
wallConfig.put("createTableAllow", "false"); // 禁止CREATE TABLE

WallConfig config = new WallConfig(wallConfig);
WallFilter wallFilter = new WallFilter();
wallFilter.setConfig(config);
ds.setProxyFilters(Arrays.asList(wallFilter));

四、两者对比

为了方便大家对比,我用一张表格把两者的差距列出来:

对比维度

HikariCP

Druid

设计理念

极简高性能,“跑车”

功能全面可监控,“SUV”

性能表现

⭐⭐⭐⭐⭐ 连接获取<5ms

⭐⭐⭐ 连接获取10-25ms

内存占用

~130KB,极度轻量

~2MB,功能丰富

TPS峰值

15万+/秒

8万-12万/秒

监控能力

基础统计

可视化仪表盘 + SQL级别追踪

SQL防火墙

不支持

支持(防注入、黑白名单)

连接泄漏检测

日志输出堆栈

自动回收 + 堆栈定位

SQL执行分析

需配合APM

内置,实时查看

五、到底该怎么选?——实战选型指南

说到这里,大家心里可能已经有个数了。

没有绝对的“更好”,只有“更适合”

我跟大家分享一个决策框架,帮你快速做出选择:

下面分场景来说:

5.1 选HikariCP的场景

  • 微服务/云原生架构:对启动速度和内存占用非常敏感,HikariCP的轻量化是最合适的。
  • 高并发交易系统:秒杀、抢购、支付场景下,每一毫秒都很关键,HikariCP延迟最低。
  • Spring Boot新项目:Spring Boot 2.x+默认就是HikariCP,直接用就好,少一个依赖就少一点维护成本。
  • 资源受限的环境:Docker容器内存分配很小的时候,HikariCP更友好。

5.2 选Druid的场景

  • 企业级后台管理系统:需要做SQL审计、性能分析,监控功能不可或缺。
  • 金融/医疗等合规领域:操作必须留痕,需要SQL防火墙防注入和操作审计。
  • 旧系统改造/运维:线上问题多、难以排查,Druid的监控能极大提升排查效率。
  • 团队规模较大、需要统一监控平台:Druid的可视化监控面板可以让DBA和开发人员共用。

5.3 给大家一个更详细的选型对比表

场景

推荐方案

核心原因

微服务/云原生

HikariCP

低内存开销,快速启动

高并发交易/秒杀

HikariCP

纳秒级连接获取延迟

大型电商平台

Druid

需要深度监控和问题诊断

金融/医疗系统

Druid

SQL审计 + 安全防护能力

旧系统运维改造

Druid

可视化监控帮你找到问题

初创项目/快速原型

HikariCP

轻量、简单、够用

总结

写这篇文章是希望对还在纠结选哪个连接池的朋友们有所帮助。

不要为了“看起来很厉害”的技术而选型,要从自己的业务需求出发。

用一句话来概括的话:

如果追求极致性能,选HikariCP——“跑车”轻快,直道狂飙;如果需要深度监控和安全防护,选Druid——“SUV”稳重,山路不慌。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 苏三说技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、先来聊聊数据库连接池是干啥的
  • 二、HikariCP到底有多快?
    • 2.1 一个简单的性能测试
    • 2.2 揭开HikariCP性能的秘密
    • 2.3 连接池大小的黄金法则
  • 三、Druid凭什么还能占据半壁江山?
    • 3.1 Druid最核心的优势——监控能力
    • 3.2 Druid的“杀手锏”
    • 3.3 SQL防火墙——防注入的“守门员”
  • 四、两者对比
  • 五、到底该怎么选?——实战选型指南
    • 5.1 选HikariCP的场景
    • 5.2 选Druid的场景
    • 5.3 给大家一个更详细的选型对比表
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档