[Java项目实战系列]从0实现数据库连接池
本文给大家介绍为何需要数据库连接池,以及从0实现数据库连接池,代码行数不足1000行,非常易懂。
全部代码已开源:
为什么需要连接池
传统数据库连接生命周期:
- 使用驱动打开数据库连接
- 打开TCP socket用于后续读写数据
- 读写数据
- 关闭连接
- 关闭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");
还在等什么!赶紧下载代码学习把~