本文给大家介绍连接池的原理,以及带大家动手从0实现一个数据库连接池。
全部源代码已开源在github,欢迎学习使用,如果觉得有用欢迎动动手指帮忙点个star~
为什么需要连接池
传统数据库连接生命周期:
- 使用驱动打开数据库连接
- 打开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类型的成员属性,用来维护连接
- DataSourceConfig和PooledDataSourceConfig定义了数据源基础配置和为了实现池化功能新增的配置
- ConnPoolException:自定义异常
- DriverUtil:驱动加载工具类
代码包结构:
测试
测试代码在类PooledDataSourceTest里。比如第一个测试方法,简单的先初始化,然后进行获取连接、释放连接的操作:
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
五月 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");
还在等什么,赶快下载源码学习吧~