浅析连接池:没有会如何?SpringBoot+Mybatis下如何接入呢?DataSource显功劳!

嘿,大家好!今天咱们来聊聊 Java 项目里绕不开的数据库连接池,从最基本的想法入手,讲讲它为啥存在、咋用、要是坏了会咋样,顺便回答几个经典问题:Druid 和 HikariCP 是啥?连接池咋嵌到代码里?关连接后咋自动回池子?还得带上 MyBatis 这种 ORM 的适配细节。咱会从简单到复杂,把问题挖出来,再看看怎么逼近现代主流方案,语气轻松点,但数字绝不能出错!


最简单的起步:直接连数据库不就行了?

假设你刚写了个 Java 小项目,要查数据库,最直白的方式是啥?直接上 JDBC,手动搞个连接:

java 复制代码
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
conn.close();

用完就关,逻辑清晰。假设你是个小博客,每天 50 个访问,数据库响应 10 毫秒,总耗时 500 毫秒,用户感觉还行。

但这朴素策略一到真实场景就露馅了:

  • 连接开销大:每次建连接得走 TCP 握手、认证啥的,假设花 50 毫秒,50 个请求就是 2.5 秒,用户等得抓狂。
  • 资源浪费:频繁开闭连接,数据库那边的 CPU 和内存扛不住,服务器日志里全是连接超时。
  • 高并发崩盘:来个 500 人同时访问,数据库默认最大连接(比如 100)不够用,后面的全卡住。

这招的毛病太明显:效率低、资源耗费高、扛不住多人访问。得想个办法优化吧?


进化一步:连接池为啥必须有?

既然老开新连接这么费劲,干脆弄个"池子",先准备好几条连接,大家轮流用,用完放回去,这就是数据库连接池的由来。Java 项目里常见的像 HikariCP、Druid、Apache DBCP,都是干这活儿的。

为啥非得有连接池?好处太多了:

  • 省时间:复用连接,免去重建的 50 毫秒,直接用只要 1 毫秒,速度快几十倍。
  • 控资源:池子大小能调,比如设 30 个最大连接,不怕把数据库挤爆。
  • 撑并发:多线程从池子里借连接,互不干扰,高峰期也不慌。

举个例子,一个电商系统每天 5 万请求,没连接池可能得建 5 万次连接,数据库早跪了。用个 20 个连接的池子复用,QPS 轻松上万,体验飞起。


连接池要是坏了,会咋样?

连接池也不是铁打的,坏了可不得了:

  • 连接漏了:代码没关连接,池子里的 20 个全被占光,新请求拿不到,系统直接僵住。
  • 连接过期:数据库断开了,池子里的连接没更新,拿出来用报"Connection reset",业务全停。
  • 配置失误:最大连接设成 5,高峰期不够用,延迟暴涨;设成 200,数据库反而被压垮。

我之前踩过坑,用 HikariCP 设了 10 个最大连接,结果 QPS 到 1500 时,平均延迟从 3 毫秒跳到 400 毫秒,查下来是线程全在排队等连接。


Druid 和 HikariCP 是啥?连接池是依赖吗?

这俩问题常被问,先澄清下:

  • Druid 是连接池:阿里开源的家伙,不光管连接,还带监控,能看连接用得多慢、多满,不是线程池,别搞混了。
  • HikariCP 也是连接池:号称 Java 最快的连接池,代码精简,专注性能,没啥花哨功能,就是管连接的。

那连接池是依赖吗?对,它以库的形式加进来,比如 Maven 的 pom.xml

xml 复制代码
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version>
</dependency>

它是底层工具,跟业务逻辑无关,专门优化数据库交互。


代码里咋嵌入?代理机制吗?

连接池咋塞进代码的?主要靠配置和封装,不一定非得动态代理,但确实有代理的影子。以 HikariCP 为例:

  1. Spring 配置 Bean

    java 复制代码
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        config.setUsername("root");
        config.setPassword("123456");
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(5);
        return new HikariDataSource(config);
    }
  2. 用的时候

    java 复制代码
    @Autowired
    private DataSource dataSource;
    
    public void query() throws SQLException {
        Connection conn = dataSource.getConnection();
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM user");
        conn.close(); // 放回池子
    }

这里 getConnection() 返回的不是原生 JDBC 连接,而是 HikariCP 包装过的对象。close() 时,它不会真断开,而是归还到池子里。这种封装有点像代理,但不是 JDK 的动态代理,更像对象池的逻辑。Druid 更明显用代理,DruidDataSource 返回的连接会拦截 close(),顺便记个监控日志。


关连接后咋自动回池子?

在 Spring 里注入连接池 Bean 后,conn.close() 为啥能自动回池子?这是因为连接池重写了 Connection 的行为。HikariCP 里,getConnection() 返回的是 HikariProxyConnection,它实现的 close() 方法不是真关,而是把连接标记为可用,放回池子的空闲队列。比如:

  • 池子有 20 个连接,5 个空闲,你拿一个用,空闲变 4 个。
  • 用完 close(),连接回到池子,空闲又变 5 个。

源码里,HikariPoolConcurrentBag 管理连接,close() 触发归还逻辑,线程安全得很。


ORM 和连接池咋适配?以 MyBatis 为例

用 MyBatis 这种 ORM,连接池咋配合?其实 MyBatis 不直接管连接池,它靠数据源(DataSource)提供连接。配置里直接指定:

xml 复制代码
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/test"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

但这用的是 MyBatis 自带的 PooledDataSource,性能一般。要换 HikariCP,可以在 Spring 里这样配:

java 复制代码
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource); // 注入 HikariCP
    return factory.getObject();
}

MyBatis 调用 dataSource.getConnection(),拿到的就是 HikariCP 的连接,用完自动归还,不需要额外适配。为啥?因为 MyBatis 只管 SQL 和映射,连接管理全交给数据源,接口标准一致就行。


优化方向:向主流方案靠拢

直接连数据库的毛病是开销大、并发差,连接池解决了这些,但还能咋改进?基于上面的例子,我总结几个点:

  1. 池子大小自适应:根据流量动态调最大连接,像 HikariCP 的插件能做到。
  2. 连接保鲜:定时检查连接状态,坏的扔掉重建,跟现代"心跳检测"一个理。
  3. 监控加持:加个仪表盘(Druid 自带,HikariCP 接 Prometheus),异常立马报警。
  4. 异步预取:高峰期提前备好连接,减少等待,往异步架构靠。

这些方向跟现在的顶尖方案,比如 HikariCP 的极致优化、Druid 的全套监控,完全对得上。

相关推荐
乔大将军1 小时前
项目准备(flask+pyhon+MachineLearning)- 1
后端·python·flask
胡图蛋.1 小时前
Spring 中哪些情况下,不能解决循环依赖问题?
java·后端·spring
ChinaRainbowSea1 小时前
8. Nginx 配合 + Keepalived 搭建高可用集群
java·运维·服务器·后端·nginx
Asthenia04122 小时前
浅谈配置Seata配置文件:tx-service-group、vgroup-mapping、data-source-proxy-mode傻傻分不清?
后端
August_._2 小时前
【Maven】基于IDEA学习 Maven依赖 与 工程继承、聚合关系
java·windows·后端·学习·maven·intellij-idea
梦兮林夕2 小时前
Go Web开发提速指南:Gin框架入门与热更新
后端·go
AskHarries3 小时前
如何利用Twilio Verify 发送验证码短信?
后端
Hamm4 小时前
巧妙使用位运算来解决真实开发中的权限控制场景
java·后端·算法
2302_799525744 小时前
【go语言】——方法集
开发语言·后端·golang