[Java项目实战系列]从0实现数据库连接池

本文给大家介绍为何需要数据库连接池,以及从0实现数据库连接池,代码行数不足1000行,非常易懂。
全部代码已开源:

为什么需要连接池

传统数据库连接生命周期:

  1. 使用驱动打开数据库连接
  2. 打开TCP socket用于后续读写数据
  3. 读写数据
  4. 关闭连接
  5. 关闭socket

可以看出数据库连接创建是一个非常昂贵的操作,我们应该尽可能避免频繁创建连接。传统的数据库访问方式,每次操作数据库都需要打开、关闭物理连接,非常耗费系统资源,性能差。而且数据库支持的连接数是有限的,创建大量的连接会导致数据库僵死。

解决方案:使用连接池,系统初始化后会创建容器,会申请一些连接对象,当需要访问数据库时,从容器中取连接对象直接使用,数据库操作结束后归还给容器。优点是节约资源,提高访问数据库的性能。

下图是使用连接池和不使用的对比:

一次SQL执行的过程:


业界连接池及对比

业界常见的连接池实现有:

  • DBCP
  • C3P0。2015年后已停止维护。
  • Druid
  • HikariCP
  • Tomcat的内置连接池

性能比较:


如何实现一个连接池

技术方案

核心要点:

  • 实现连接重用。连接使用后并不直接释放,而是归还到连接池,方便下一个请求使用。

核心步骤:

  • 初始化连接池,提前创建一定数量的空闲连接
  • 调用getConnection方法获取连接
    • 如果连接池中有空闲的连接,直接取空闲连接返回
    • 如果无,则判断当前连接池中的连接数是否超过最大阈值,如果是,等待,通过wait/notifyAll来实现
    • 如果未超过最大阈值,创建新连接,存入连接池
  • 调用returnConnection释放连接
    • 判断当前连接是否可用
    • 将连接置位空闲。

代码结构

类图:

核心类说明:

  • ILifeCycle接口定义数据源生命周期
  • IPooledConnection接口扩展自Connection,但是额外定义了为了实现池化功能新增的几个方法
  • PooledConnection是IPooledConnection接口的实现类,封装Connection,实现池化功能,可以理解为一个代理,主要是校验连接状态,将请求调用直接代理到实际的Connection
  • IPooledDataSource接口扩展自DataSource,但是额外定义了为了实现池化功能新增的几个方法
  • AbstractDataSource:数据源默认实现抽象类
  • DefaultDataSource:本次实现的池化数据源,负责连接的获取和回收,包含一个List<IPooledConnection>类型的成员属性,用来维护连接
  • DataSourceConfig和PooledDataSourceConfig定义了数据源基础配置和为了实现池化功能新增的配置
  • ConnPoolException:自定义异常
  • DriverUtil:驱动加载工具类

代码包结构:

测试

测试代码在类PooledDataSourceTest里。比如第一个测试方法,简单的先初始化,然后进行获取连接、释放连接的操作:
五月 18, 2022 8:14:14 上午 com.summer.dsconnpool.DefaultDataSource createConnection
信息: 创建新连接.....
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource getConnection
信息: getConnection begin....
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource logConnPoolDigest
信息: pooledConnectionList status:[1,busy status:[false,]]
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource getFreeConnectionFromPool
信息: 从连接池中获取连接
db_summer_1
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource getConnection
信息: getConnection begin....
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource logConnPoolDigest
信息: pooledConnectionList status:[1,busy status:[true,]]
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource getConnection
信息: 开始扩容连接池大小...
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource createConnection
信息: 创建新连接.....
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource getConnection
信息: 扩容完成...
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource logConnPoolDigest
db_summer_1
信息: pooledConnectionList status:[2,busy status:[true,true,]]
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource checkValid
信息: 开始校验连接
五月 18, 2022 8:14:15 上午 com.summer.dsconnpool.DefaultDataSource logConnPoolDigest
信息: pooledConnectionList status:[2,busy status:[false,true,]]

注意运行本次测试需要依赖本机安装mysql数据库。安装完毕后创建样例数据库。这里测试类里的相关配置可以根据需要进行修改:

dataSourceConfig.setDriverClass("com.mysql.jdbc.Driver");
dataSourceConfig.setJdbcUrl("jdbc:mysql://localhost:3306/db_summer_1?useUnicode=true&characterEncoding=utf-8");
dataSourceConfig.setUser("root");
dataSourceConfig.setPassword("summer");

还在等什么!赶紧下载代码学习把~



#面试##内推##面经##笔试题目##Java##项目#
全部评论

相关推荐

6 9 评论
分享
牛客网
牛客企业服务