# MyDBConnectionPool **Repository Path**: louxj/MyDBConnectionPool ## Basic Information - **Project Name**: MyDBConnectionPool - **Description**: 手写简易的数据库连接池 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2018-12-22 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简介 手写简易的数据库连接池 # 前言 数据库连接池的基本思想是:为数据库连接建立一个“缓冲池”,预先在池中放入一定数量的数据库连接管道,需要时,从池子中取出管道进行使用,操作完毕后,在将管道放入池子中,从而避免了频繁的向数据库申请资源,释放资源带来的性能损耗。在如今的分布式系统当中,系统的QPS瓶颈往往就在数据库,所以理解数据库连接池底层构造原理与设计思想是很有益处的。我们常用的数据库连接池有C3P0,DBCP,Druid等,下面我们就来分析下数据库连接池应该有些什么,以及手写一个迷你版的数据库连接池! # 思考 ![数据库连接池](./pic/数据库连接池.png) 第一,数据库连接池中存放的就是数据库操作管道,不仅仅是存放,而且应该是管理这些管道; 第二,应该提供外部配置文件去初始化数据库连接池; 第三,如果一个数据库操作管道已经被占用,那么其他请求是否应该得到这个管道,也就是说我们要考虑多线程并发下,管道的分配问题; 第四,如果做到管道的复用?放回池子中,标示可用,并不是真正的关闭管道; # 实现 ![](./pic/关键类说明.png) - IMyPool是一个接口,对外提供数据库连接池的基本服务,比如得到一个数据库操作管道。 - MyDefaultPool是IMyPool的实现。 - MyPooledConnection代表数据库操作管道,它可以执行SQL,关闭管道等。 - MyPoolFactory是一个工厂,单例模式,用于得到IMyPool实现。 - DBConfigXML代表外部配置文件。 - BasicTest/PoolTest用于测试。 ## IMyPool ```java /** * 连接池接口类 *

* 对外提供数据库连接池的基本服务,比如得到一个数据库操作管道。 */ public interface IMyPool { // 获取一个连接 MyPooledConnection getMyPooledConnection(); // 新建指定数目的连接数 void createMyPooledConnection(int count); } ``` ## MyDefaultPool ```java import com.netease.connectionpool.config.DBConfigXML; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Vector; /** * 连接池 * 获取外部配置信息进行初始化 * 加载数据驱动 */ public class MyDefaultPool implements IMyPool { //MyDefaultPool持有一个管道集合,基于多线程的考虑,这里使用了Vector。 private Vector myPooledConnectionVector = new Vector(); // 数据库驱动 private String jdbcDriver; // 数据库访问地址 private String jdbcURL; // 数据库连接用户名 private String jdbcUsername; // 数据库连接密码 private String jdbcPassword; // 数据库连接池连接数初始化大小 private int initCount; // 连接池中连接数不足时增长的步进数 private int step; // 连接池中最大连接数 private int maxCount; // 构造函数:数据库连接池需要根据外部配置文件完成数据库驱动加载以及初始化管道的建立。 public MyDefaultPool() { // 初始胡数据库连接池配置 init(); // 加载数据库驱动程序 try { // 加载MYSQL JDBC驱动程序 Class.forName(this.jdbcDriver); System.out.println("Success loading Mysql Driver!"); } catch (ClassNotFoundException e) { System.out.print("Error loading Mysql Driver!"); e.printStackTrace(); } // 初始化数据库连接池管道 createMyPooledConnection(initCount); } private void init() { this.jdbcDriver = DBConfigXML.jdbcDriver; this.jdbcURL = DBConfigXML.jdbcURL; this.jdbcUsername = DBConfigXML.jdbcUsername; this.jdbcPassword = DBConfigXML.jdbcPassword; this.initCount = DBConfigXML.initCount; this.step = DBConfigXML.step; this.maxCount = DBConfigXML.maxCount; } /** * 数据库连接池在创建管道时,应该去看一下是否达到上限,如果没有,则可以创建。 *

* 不仅仅要创建出来,还要标示每一个管道的isBusy标志。 * * @param count */ public void createMyPooledConnection(int count) { if (myPooledConnectionVector.size() > maxCount || myPooledConnectionVector.size() + count > maxCount) { throw new RuntimeException("连接池已满"); } for (int i = 0; i < count; ++i) { try { Connection connection = DriverManager.getConnection(jdbcURL, jdbcUsername, jdbcPassword); MyPooledConnection myPooledConnection = new MyPooledConnection(connection, false); myPooledConnectionVector.add(myPooledConnection); } catch (Exception e) { e.printStackTrace(); } } } //如果得不到操作管道,需要去创建管道! public MyPooledConnection getMyPooledConnection() { if (myPooledConnectionVector.size() < 1) { throw new RuntimeException("连接池初始化参数错误"); } MyPooledConnection myPooledConnection = null; try { myPooledConnection = getRealConnectionFromPool(); while (myPooledConnection == null) { createMyPooledConnection(step); myPooledConnection = getRealConnectionFromPool(); return myPooledConnection; } } catch (SQLException e) { e.printStackTrace(); } return myPooledConnection; } /** * 第一,这里使用了synchronized,就是为了避免多线程下产生问题。 *

* 第二,要知道Connection是有超时机制的,如果我们得到的管道的Connection已经超时了怎么办呢? *

* 第三,得到管道后,一定注意isBusy的设置。 * * @return * @throws SQLException */ private synchronized MyPooledConnection getRealConnectionFromPool() throws SQLException { MyPooledConnection myPooledConnection = null; for (MyPooledConnection connection : myPooledConnectionVector) { if (!connection.isBusy()) { if (connection.getConnection().isValid(3000)) { connection.setBusy(true); myPooledConnection = connection; } else { try { Connection con = DriverManager.getConnection(jdbcURL, jdbcUsername, jdbcPassword); connection.setConnection(con); connection.setBusy(true); myPooledConnection = connection; } catch (Exception e) { e.printStackTrace(); } } } } return myPooledConnection; } } ``` ## MyPooledConnection ```java ** * 封装Connection,提供基本的SQL查询能力 * 数据库连接管道,就是对JDBC Connection进行封装而已,但是需要注意busy的这个标示。 * 连接池中的关闭连接实际上是将连接放回到连接池中以便其他使用者复用,实际上只是标示的改变而已! */ public class MyPooledConnection { private Connection connection; private boolean busy; public MyPooledConnection(Connection connection, boolean busy) { this.connection = connection; this.busy = busy; } public void close() { this.busy = false; } public Connection getConnection() { return connection; } public void setConnection(Connection connection) { this.connection = connection; } public boolean isBusy() { return busy; } public void setBusy(boolean busy) { this.busy = busy; } public ResultSet query(String sql) { Statement statement; ResultSet resultSet = null; try { statement = connection.createStatement(); resultSet = statement.executeQuery(sql); } catch (Exception e) { e.printStackTrace(); } return resultSet; } } ``` ## MyPoolFactory ```java /** * 数据库连接池工厂 *

* 单例模式 */ public class MyPoolFactory { private static IMyPool myPool = new MyDefaultPool(); private MyPoolFactory() { } public static IMyPool getInstance() { return myPool; } } ``` # 源码地址 [MyDBConnectionPool](https://gitee.com/louxj/MyDBConnectionPool/tree/master)