暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

数据库连接池实现原理

卓京IT之家 2018-06-01
297

为什么要用数据库连接池
数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。

连接池的基本概念

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:

  1. 最小连接数
    是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。

  2. 最大连接数
    是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。

  3. 最小连接数与最大连接数差距
    最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。

主流开源数据库连接池

1、C3P0:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate 一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
2、Proxool:是一个Java SQL Driver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。
3、Jakarta DBCP:DBCP是一个依赖Jakartacommons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用。
4、DDConnectionBroker:是一个简单、轻量级的数据库连接池。
5、DBPool:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池。
6、XAPool:是一个XA数据库连接池。它实现了javax.sql.XADataSource并提供了连接池工具。
7、Primrose:是一个Java开发的数据库连接池。当前支持的容器包括Tomcat4&5、Resin3与JBoss3。它同样也有一个独立的版本,可以在应用程序中使用而不必运行在容器中。Primrose通过一个WEB接口来控制SQL处理的追踪、配置,以及动态池管理。在重负荷的情况下可进行连接请求队列处理。
8、SmartPool:是一个连接池组件,它模仿应用服务器对象池的特性。SmartPool能够解决一些临界问题如连接泄漏(connection leaks)、连接阻塞、打开的JDBC对象(如Statements、PreparedStatements)等。SmartPool的特性包括:
支持多个pool
自动关闭相关联的JDBC对象
在所设定time-outs之后察觉连接泄漏
追踪连接使用情况
强制启用最近最少用到的连接
把SmartPool“包装”成现存的一个pool
9、MiniConnectionPoolManager:是一个轻量级JDBC数据库连接池。它只需要Java1.5(或更高)并且没有依赖第三方包。
10、BoneCP:是一个快速、开源的数据库连接池。帮用户管理数据连接,让应用程序能更快速地访问数据库。比C3P0/DBCP连接池速度快25倍。
11、Druid:Druid不仅是一个数据库连接池,还包含一个ProxyDriver、一系列内置的JDBC组件库、一个SQL Parser。
支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等。
Druid针对Oracle和MySql做了特别优化,比如:
Oracle的PS Cache内存占用优化
MySql的ping检测优化
Druid提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支持,这是一个手写的高性能SQL Parser,支持Visitor模式,使得分析SQL的抽象语法树很方便。
简单SQL语句用时10微秒以内,复杂SQL用时30微秒。
通过Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如说分库分表、审计等。Druid防御SQL注入攻击的WallFilter,就是通过Druid的SQL Parser分析语义实现的
·······························邪恶的代码分界线········································

源码地址:https://github.com/java-rookie-233333/ConnectionPool

首先我们自己先定义一个SuperDataSource类去实现javax.sql.DataSource和

javax.sql.ConnectionPoolDataSource接口,继承的ResourceManagement类是我用来把一需要在SuperDataSource实现的方法转移到父类中去实现

然后我们在定义一个DatabaseConfiguration类,这个类是用来保存数据库连接池的一些设置信息,这个是我在获取数据库连接时候需要用到的信息

这里的信息是在SuperDataSource进行了一个初始化设置

在这里我进行了数据库的信息保存,然后设置到SuperPool中;

SuperDataSource类代码

package com;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import com.pool.SuperPool;
import com.pool.SuperPoolConnection;
import com.uitl.StringUtils;
public class SuperDataSource extends ResourceManagement implements DataSource,ConnectionPoolDataSource {
   protected volatile String username;
   protected volatile String password;
   protected volatile String jdbcUrl;
   protected volatile String driverClass;
   protected volatile int initialSize = 0;
   protected volatile int minIdle = 0;
   protected volatile int maxIdle = 0;
   protected volatile int maxActive = 0;
   protected volatile long maxWait = 0;
   protected volatile long waitTime = 1000L;
   private SuperPool superPool = SuperPool.getInstance();
   public SuperDataSource() {
   }
   public SuperDataSource(String username, String password, String jdbcUrl) {
       super();
       this.username = username;
       this.password = password;
       this.jdbcUrl = jdbcUrl;
   }
   public SuperPoolConnection getSuperPoolConnection() {
       init();
       try {
           return superPool.getSuperPoolConnection(getWaitTime());
       } catch (InterruptedException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       return null;
   }
   public Connection getConnection() throws SQLException {
       SuperPoolConnection superPoolConnection = getSuperPoolConnection();
       if(superPoolConnection!=null) {
           return superPoolConnection;
       }else {
           throw new UnsupportedOperationException("Not supported by SuperDataSource");
       }
   }
   public Connection getConnection(String username, String password) throws SQLException {
       if (this.username == null && this.password == null && username != null && password != null) {
           this.username = username;
           this.password = password;
           return getConnection();
       }
       if (!StringUtils.equals(username, this.username)) {
           throw new UnsupportedOperationException("Not supported by SuperDataSource");
       }
       if (!StringUtils.equals(password, this.password)) {
           throw new UnsupportedOperationException("Not supported by SuperDataSource");
       }
       return getConnection();
   }
   private void init() {
       if(superPool.getConfiguration() == null) {
           DatabaseConfiguration configuration = new DatabaseConfiguration();
           configuration.setDriverClass(getDriverClass());
           configuration.setJdbcUrl(getJdbcUrl());
           configuration.setUsername(getUsername());
           configuration.setPassword(getPassword());
           configuration.setInitialSize(getInitialSize());
           configuration.setMaxActive(getMaxActive());
           configuration.setMaxIdle(getMaxIdle());
           configuration.setMaxWait(getMaxWait());
           configuration.setMinIdle(getMinIdle());
           superPool.setConfiguration(configuration);
       }
   }
   public PooledConnection getPooledConnection() throws SQLException {
       SuperPoolConnection superPoolConnection = getSuperPoolConnection();
       if(superPoolConnection!=null) {
           return superPoolConnection;
       }else {
           throw new UnsupportedOperationException("Not supported by SuperDataSource");
       }
   }
   public PooledConnection getPooledConnection(String user, String password) throws SQLException {
       if (this.username == null && this.password == null && username != null && password != null) {
           this.username = username;
           this.password = password;
           return getPooledConnection();
       }
       if (!StringUtils.equals(username, this.username)) {
           throw new UnsupportedOperationException("Not supported by SuperDataSource");
       }
       if (!StringUtils.equals(password, this.password)) {
           throw new UnsupportedOperationException("Not supported by SuperDataSource");
       }
       return getPooledConnection();
   }
   //关闭连接池的时候这里要去进行连接集合的清理,目前未做实现
   public void close() throws SQLException {
       // TODO Auto-generated method stub
   }
//****************参数get,set方法分界线******************************************    
   public String getUsername() {
       return username;
   }
   public void setUsername(String username) {
       this.username = username;
   }
   public String getPassword() {
       return password;
   }
   public void setPassword(String password) {
       this.password = password;
   }
   public String getJdbcUrl() {
       return jdbcUrl;
   }
   public void setJdbcUrl(String jdbcUrl) {
       this.jdbcUrl = jdbcUrl;
   }
   public String getDriverClass() {
       return driverClass;
   }
   public void setDriverClass(String driverClass) {
       this.driverClass = driverClass;
   }
   public int getInitialSize() {
       return initialSize;
   }
   public void setInitialSize(int initialSize) {
       this.initialSize = initialSize;
   }
   public int getMinIdle() {
       return minIdle;
   }
   public void setMinIdle(int minIdle) {
       this.minIdle = minIdle;
   }
   public int getMaxIdle() {
       return maxIdle;
   }
   public void setMaxIdle(int maxIdle) {
       this.maxIdle = maxIdle;
   }
   public int getMaxActive() {
       return maxActive;
   }
   public void setMaxActive(int maxActive) {
       this.maxActive = maxActive;
   }
   public long getMaxWait() {
       return maxWait;
   }
   public void setMaxWait(long maxWait) {
       this.maxWait = maxWait;
   }
   public long getWaitTime() {
       return waitTime;
   }
   public void setWaitTime(long waitTime) {
       this.waitTime = waitTime;
   }
}

然后定义一个SuperPool类,以下是该类方法属性预览图

SuperPool(这是一个单列模式的类,只会加载一次)

1.initDriver():初始化加载驱动,加载一次后就不会再进行加载

2.getConnection() 获取连接

3.getSuperPoolConnection(long waitTime)获取可用连接(具体实现思路在代码中进行补充)

package com.pool;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.ArrayBlockingQueue;
import com.DatabaseConfiguration;
public class SuperPool {
   // 在SuperDataSource.getSuperPoolConnection() 方法中进初始化设置
   private DatabaseConfiguration configuration;
   // 已使用连接
   private ArrayBlockingQueue<SuperPoolConnection> use = new ArrayBlockingQueue<SuperPoolConnection>(1024);
   // 未使用连接
   private ArrayBlockingQueue<SuperPoolConnection> duse = new ArrayBlockingQueue<SuperPoolConnection>(1024);
   // 驱动是否初始化标志位
   private boolean initDriver = false;
   private SuperPool() {
   }
   // 初始化驱动方法,只会加载一次
   private void initDriver() {
       try {
           Class.forName(configuration.getDriverClass());
       } catch (ClassNotFoundException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       // 初始化成功设置为true
       initDriver = true;
   }
   private SuperPoolConnection getConnection() {
       // 判断驱动是否初始化加载
       if (!initDriver) {
           initDriver();
       }
       Connection conn = null;
       // 自定义的一个Connection类,实现了javax.sql.PooledConnection, java.sql.Connection 接口
       SuperPoolConnection superPoolConnection = null;
       try {
           conn = DriverManager.getConnection(configuration.getJdbcUrl(), configuration.getUsername(),
                   configuration.getPassword());
           // 将获取到的连接设置到自定义的Connection实现类中
           superPoolConnection = new SuperPoolConnection(conn);
       } catch (SQLException e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
       return superPoolConnection;
   }
   // 单列模式加载
   private static class Singteton {
       private static SuperPool superPool;
       static {
           superPool = new SuperPool();
       }
       public static SuperPool getInstance() {
           return superPool;
       }
   }
   public static SuperPool getInstance() {
       return Singteton.getInstance();
   }
   public DatabaseConfiguration getConfiguration() {
       return configuration;
   }
   public void setConfiguration(DatabaseConfiguration configuration) {
       this.configuration = configuration;
   }
   // 获取连接
   public SuperPoolConnection getSuperPoolConnection(long waitTime) throws InterruptedException {
       SuperPoolConnection superPoolConnection = null;
       while (true) {
           // 如果可用连接和已用连接加起来的数量小于最大连接,才允许去获取连接,否则设置时间挂起
           if (use.size() + duse.size() <= getConfiguration().getMaxIdle()) {
               // 如果可用连接小于等于0那就新获取一个连接,返回之前把这个连接放到已用集合中去
               if (duse.size() <= 0) {
                   superPoolConnection = getConnection();
                   use.put(superPoolConnection);
                   return superPoolConnection;
               } else {
                   return duse.take();
               }
           } else {
               wait(waitTime);
           }
       }
   }
   // 增加一个连接到已用连接集合中去
   public void addUse(SuperPoolConnection use) {
       this.use.add(use);
   }
   // 移除一个已用连接
   public void removeUse(SuperPoolConnection use) {
       this.use.remove(use);
   }
   // 增加一个可用连接
   public void addDuse(SuperPoolConnection duse) {
       this.duse.add(duse);
   }
   // 移除一个可用连接
   public void removeDuse(SuperPoolConnection duse) {
       this.duse.remove(duse);
   }
}

SuperPoolConnection类,实现了javax.sql.PooledConnection, java.sql.Connection接口,在这个类中进行了连接的回收

package com.pool;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import javax.sql.ConnectionEventListener;
import javax.sql.StatementEventListener;
public class SuperPoolConnection  implements javax.sql.PooledConnection, java.sql.Connection {
   protected Connection connection;
   //这个类为单列,在此处不会重复加载,还是原来的第一次加载的对象
   private SuperPool superPool = SuperPool.getInstance();
   //连接是否关闭标志位
   boolean isClosed = false;
   //在类初始化的时候要求传入一个java.sql.Connection,这是在SuperPool.getConnection() 中进行了set
   public SuperPoolConnection(Connection connection) {
       this.connection = connection;
   }
   //这里去判断接口否关闭的方法进行了自定义的转态控制
   public boolean isClosed() throws SQLException {
       return isClosed;
   }
   //关闭此连接时会进行连接回收操作
   public void close() throws SQLException {
       //将当前对象在已用连接集合中进行移除
       superPool.removeUse(this);
       //然后在将当前对象加到可用连接集合中
       superPool.addDuse(this);
       //次连接的转态Close转态设置为true
       isClosed = true;
       //再将连接设置为null
       connection = null;
   }
   public <T> T unwrap(Class<T> iface) throws SQLException {
       return connection.unwrap(iface);
   }
   public boolean isWrapperFor(Class<?> iface) throws SQLException {
       // TODO Auto-generated method stub
       return connection.isWrapperFor(iface);
   }
   public Statement createStatement() throws SQLException {
       // TODO Auto-generated method stub
       return connection.createStatement();
   }
   public PreparedStatement prepareStatement(String sql) throws SQLException {
       // TODO Auto-generated method stub
       return connection.prepareStatement(sql);
   }
   public CallableStatement prepareCall(String sql) throws SQLException {
       return connection.prepareCall(sql);
   }
   public String nativeSQL(String sql) throws SQLException {
       return connection.nativeSQL(sql);
   }
   public void setAutoCommit(boolean autoCommit) throws SQLException {
       connection.setAutoCommit(autoCommit);      
   }
   public boolean getAutoCommit() throws SQLException {
       return connection.getAutoCommit();
   }
   public void commit() throws SQLException {
       connection.commit();        
   }
   public void rollback() throws SQLException {
       connection.rollback();      
   }
   public DatabaseMetaData getMetaData() throws SQLException {
       // TODO Auto-generated method stub
       return connection.getMetaData();
   }
   public void setReadOnly(boolean readOnly) throws SQLException {
       connection.setReadOnly(readOnly);      
   }
   public boolean isReadOnly() throws SQLException {
       return connection.isReadOnly();
   }
   public void setCatalog(String catalog) throws SQLException {
       connection.setCatalog(catalog);
   }
   public String getCatalog() throws SQLException {
       return connection.getCatalog();
   }
   public void setTransactionIsolation(int level) throws SQLException {
       connection.setTransactionIsolation(level);      
   }
   public int getTransactionIsolation() throws SQLException {
       return connection.getTransactionIsolation();
   }
   public SQLWarning getWarnings() throws SQLException {
       return connection.getWarnings();
   }
   public void clearWarnings() throws SQLException {
       connection.clearWarnings();
   }
   public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
       return connection.createStatement(resultSetType, resultSetConcurrency);
   }
   public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
           throws SQLException
{
       return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
   }
   public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
       return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
   }
   public Map<String, Class<?>> getTypeMap() throws SQLException {
       return connection.getTypeMap();
   }
   public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
       connection.setTypeMap(map);    
   }
   public void setHoldability(int holdability) throws SQLException {
       connection.setHoldability(holdability);    
   }
   public int getHoldability() throws SQLException {
       return connection.getHoldability();
   }
   public Savepoint setSavepoint() throws SQLException {
       return connection.setSavepoint();
   }
   public Savepoint setSavepoint(String name) throws SQLException {
       return connection.setSavepoint(name);
   }
   public void rollback(Savepoint savepoint) throws SQLException {
       connection.rollback(savepoint);    
   }
   public void releaseSavepoint(Savepoint savepoint) throws SQLException {
       connection.releaseSavepoint(savepoint);
   }
   public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
           throws SQLException
{
       return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
   }
   public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
           int resultSetHoldability)
throws SQLException
{
       return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
   }
   public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
           int resultSetHoldability)
throws SQLException
{
       return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
   }
   public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
       return connection.prepareStatement(sql, autoGeneratedKeys);
   }
   public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
       return connection.prepareStatement(sql, columnIndexes);
   }
   public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
       return connection.prepareStatement(sql, columnNames);
   }
   public Clob createClob() throws SQLException {
       return connection.createClob();
   }
   public Blob createBlob() throws SQLException {
       return connection.createBlob();
   }
   public NClob createNClob() throws SQLException {
       return connection.createNClob();
   }
   public SQLXML createSQLXML() throws SQLException {
       return connection.createSQLXML();
   }
   public boolean isValid(int timeout) throws SQLException {
       return connection.isValid(timeout);
   }
   public void setClientInfo(String name, String value) throws SQLClientInfoException {
       connection.setClientInfo(name, value);      
   }
   public void setClientInfo(Properties properties) throws SQLClientInfoException {
       connection.setClientInfo(properties);      
   }
   public String getClientInfo(String name) throws SQLException {
       return connection.getClientInfo(name);
   }
   public Properties getClientInfo() throws SQLException {
       return connection.getClientInfo();
   }
   public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
       return connection.createArrayOf(typeName, elements);
   }
   public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
       return connection.createStruct(typeName, attributes);
   }
   public void setSchema(String schema) throws SQLException {
       connection.setSchema(schema);      
   }
   public String getSchema() throws SQLException {
       return connection.getSchema();
   }
   public void abort(Executor executor) throws SQLException {
       connection.abort(executor);    
   }
   public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
       connection.setNetworkTimeout(executor, milliseconds);      
   }
   public int getNetworkTimeout() throws SQLException {
       return connection.getHoldability();
   }
   public Connection getConnection() throws SQLException {
       return connection;
   }
   public void addConnectionEventListener(ConnectionEventListener listener) {
       // TODO Auto-generated method stub
   }
   public void removeConnectionEventListener(ConnectionEventListener listener) {
       // TODO Auto-generated method stub
   }
   public void addStatementEventListener(StatementEventListener listener) {
       // TODO Auto-generated method stub
   }
   public void removeStatementEventListener(StatementEventListener listener) {
       // TODO Auto-generated method stub
   }
}

代码测试1

这里我们会发现第一次获取连接如果使用完后进行了关闭,第二次进行获取的时候,获取的是同一个连接对象

测试代码2

如果我们第一次获取连接为进行关闭,那么第二次进行连接获取就是新获取一个连接


150305刘宏斌


加我微信

本人现从事医疗互联网软件开发,在公司是一名全栈工程师主要负责java后端开发,有时会编写python。目前在专攻机器深度学习,希望能和志同道合的小伙伴们一起共同学习。                                    ——刘宏斌


文章转载自卓京IT之家,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论