数据库连接池优雅设计与手写实现

1、数据库连接池是什么?

数据库连接是一种昂贵的资源,创建数据库连接是一个耗时的操作,在多线程并发条件下尤为突出,对数据库连接的高效管理能影响到程序的性能指标,数据库连接池正是针对这个问题提出来的。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个新连接,利用数据库连接池将明显提高对数据库操作的性能;

2、数据库连接池有哪些技术方案?

(1)C3P0

(2)DBCP

(3)Proxool

(4)Tomcat Jdbc Pool

(5)BoneCP

(6)Druid

(7)HiKariCP

数据库连接池属于一种池化技术;

池化技术:http访问(httpclient)、redis访问(redisPool)、线程(线程池)等;

面试:(有现场需要写代码的面试)

1、手写一个队列(不能使用jdk自带的工具类)

2、手写一个栈

3、手写一个HashMap

4、手写一个生产者消费者

5、手写一个(jvm)缓存,甚至不能用jdk中现有的类

6、......

纯手写数据库连接池实现

------MyDataSourceInterface

package com.atqiyu.pool;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;

/**
 * @author 7575
 * @version 1.0
 * 我亦无他,唯手熟尔!
 */
public interface MyDataSourceInterface extends DataSource {
    @Override
    default Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    default Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    //下面的方法可以不实现


    @Override
    default <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    default boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    default PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    default void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    default void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    default int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    default Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

------MyAbstractDataSource

package com.atqiyu.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * @author 7575
 * @version 1.0
 * 我亦无他,唯手熟尔!
 */
public abstract class MyAbstractDataSource implements MyDataSourceInterface {

    private String url;

    private String driver;

    private String user;

    private String password;

    //最大的正在使用的连接数
    private int poolMaxActiveConnections = 10;

    //最大的空闲连接数
    private int poolMaxIdleConnections = 5;

    //从连接池中获取一个连接最大等待多少毫秒
    private int poolTimeToWait = 30000;

    public int getPoolMaxActiveConnections() {
        return poolMaxActiveConnections;
    }

    public void setPoolMaxActiveConnections(int poolMaxActiveConnections) {
        this.poolMaxActiveConnections = poolMaxActiveConnections;
    }

    public int getPoolMaxIdleConnections() {
        return poolMaxIdleConnections;
    }

    public void setPoolMaxIdleConnections(int poolMaxIdleConnections) {
        this.poolMaxIdleConnections = poolMaxIdleConnections;
    }

    public int getPoolTimeToWait() {
        return poolTimeToWait;
    }

    public void setPoolTimeToWait(int poolTimeToWait) {
        this.poolTimeToWait = poolTimeToWait;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public Connection getConnection() throws SQLException {
        return getConnection(user, password);
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return doGetConnection(username, password);
    }

    /**
     * 获取数据库连接
     *
     * @param username
     * @param password
     * @return
     */
    private Connection doGetConnection(String username, String password) throws SQLException {
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }
}

------MyDataSource

package com.atqiyu.pool;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 7575
 * @version 1.0
 * 我亦无他,唯手熟尔!
 *
 * 数据源的连接池
 */
public class MyDataSource extends MyAbstractDataSource {

    //空闲连接池
    private final List<ConnectionProxy> idleConnections = new ArrayList<ConnectionProxy>();

    //激活的连接池
    private final List<ConnectionProxy> activeConnections = new ArrayList<ConnectionProxy>();



    //监视器对象 用于作一些同步操作相关的一些使用
    private final Object monitor = new Object();

    //监视器对象 用于作一些同步操作相关的一些使用
    private final Object watch = new Object();

    /**
     * 覆盖父类的方法,返回的是一个代理连接
     * @return
     * @throws SQLException
     */
    @Override
    public Connection getConnection() throws SQLException {

        ConnectionProxy connectionProxy = getConnectionProxy(super.getUser(),super.getPassword());

        return connectionProxy.getProxyConnection();
    }

    /**
     * 获取连接
     * @param username
     * @param password
     * @return
     */
    public ConnectionProxy getConnectionProxy(String username,String password) throws SQLException {
        boolean wait = false;
        ConnectionProxy connectionProxy = null;

        //刚开始是没有连接的
        while (connectionProxy == null) {
            //做一个同步线程
            synchronized (monitor) {

                //如果空闲连接不为空,那么就可以直接获取到连接
                if (!idleConnections.isEmpty()) {
                    connectionProxy = idleConnections.remove(0);
                } else {
                    //没有空闲连接可以使用,那么我们需要获取新的连接(也就是需要创建一个连接)
                    if (activeConnections.size() < super.getPoolMaxActiveConnections()) {
                        //如果当前激活的连接数 小于 我们允许的最大连接数,那么此时可以创建一个新的连接,否则还不能创建
                        connectionProxy = new ConnectionProxy(super.getConnection(),this);
                    }
                    //否则是不能创建新连接的,需要等待,等  private int poolTimeToWait = 30000; 毫秒

                }
            }

            if (!wait) {
                wait = true;
            }

            if (connectionProxy == null) {
                try {
                    //连接对象是空,那么需要等待;
                    monitor.wait(super.getPoolTimeToWait());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //万一等待被循环打断,退出一下循环
                    break;
                }
            }
        }


        if (connectionProxy != null) {
            //连接对象不为空,说明已经拿到连接了
            activeConnections.add(connectionProxy);
        }
        //返回连接对象
        return connectionProxy;
    }

    /**
     * 关闭连接,但是不是把连接关闭,而是把连接池归还到连接池
     * @param connectionProxy
     */
    public void closeConnection(ConnectionProxy connectionProxy) {
        synchronized (monitor) {
            //关闭连接,那么就是把激活状态的连接,变成空闲连接
            activeConnections.remove(connectionProxy);

            if (idleConnections.size() < super.getPoolMaxIdleConnections()) {
                idleConnections.add(connectionProxy);
            }

            //通知一下,唤醒上面那个等待获取连接的线程
            monitor.notifyAll();
        }
    }

}

------ConnectionProxy

package com.atqiyu.pool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

/**
 * @author 7575
 * @version 1.0
 * 我亦无他,唯手熟尔!
 *
 * 采用动态代理实现对数据库连接的代理
 */
public class ConnectionProxy implements InvocationHandler {

    //真实的连接
    private Connection realConnection;

    //代理连接
    private Connection proxyConnection;

    //持有数据源对象
    private MyDataSource myDataSource;

    public Connection getRealConnection() {
        return realConnection;
    }

    public void setRealConnection(Connection realConnection) {
        this.realConnection = realConnection;
    }

    public Connection getProxyConnection() {
        return proxyConnection;
    }

    public void setProxyConnection(Connection proxyConnection) {
        this.proxyConnection = proxyConnection;
    }

    public MyDataSource getMyDataSource() {
        return myDataSource;
    }

    public void setMyDataSource(MyDataSource myDataSource) {
        this.myDataSource = myDataSource;
    }

    /**
     * 构造方法
     *
     * @param realConnection
     * @param myDataSource
     */
    public ConnectionProxy(Connection realConnection, MyDataSource myDataSource) {
        //初始化真实连接
        this.realConnection = realConnection;
        //初始化数据源
        this.myDataSource = myDataSource;

        //初始化代理连接
        this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(),
                       new Class<?>[] {Connection.class},
                       this);

    }

    /**
     * 当调用Connection对象里面的方法时,首先会被该invoke方法拦截
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取到当前调用了Connection对象的什么方法
        String methodName = method.getName();

        if (methodName.equalsIgnoreCase("close")) {
            //TODO 把连接归还到连接池
            myDataSource.closeConnection(this);
            return null;
        } else {
            return method.invoke(realConnection,args);
        }

    }
}

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值