一.什么是连接池

     和线程池类似,为了避免数据库连接频繁建立、关闭的开销,在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。
      外部使用者可通过getConnection 方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,由连接池管理器回收。

二.为啥使用连接池

      节省创建连接与释放连接性能消耗,连接池中连接起到复用的作用,提高程序性能。
      减少系统响应时间,程序启动的时候,就已建立数据库连接池,对于业务请求可以直接利用现有可用的连接,避免再次连接和手动释放连接。  

    三.如何创建连接池

      javax.sql包下的 DataSource接口,getConnection()用来获取一个连接对象Connection。所以,支持java的连接池都实现了javax.sql.DataSource接口。用来创建连接池,目前可用的连接池有C3P0、Druid等,可通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。

四.JDBC

1.什么是JDBC

  JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成,是一种约定。

2.数据库驱动

  我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。

   

pymysql连接池多线程 查询 mysql连接池和线程池_数据库

                                                                        图1.1

引用菜鸟教程上的一段代码

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class DbUtil {

    public static final String URL = "jdbc:mysql://localhost:3306/imooc";
    public static final String USER = "liulx";
    public static final String PASSWORD = "123456";

    public static void main(String[] args) throws Exception {
        //1.加载驱动程序
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获得数据库连接
        Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
        //3.操作数据库,实现增删改查
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT user_name, age FROM imooc_goddess");
        //如果有数据,rs.next()返回true
        while(rs.next()){
            System.out.println(rs.getString("user_name")+" 年龄:"+rs.getInt("age"));
        }
    }
}

想操作数据库,有2个步骤是必须的,

1:加载MySQL驱动

2:获取数据库连接

那么为啥需要先加载驱动呢,我们先进mysql Driver源码看看,这里实现了java.sql.Driver,并且在类加载的时候调用了静态方法,向DriverManager里注册了驱动

package com.mysql.cj.jdbc;

import java.sql.SQLException;


public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    // 静态方法 调用Class.forName("com.mysql.jdbc.Driver")的时候向DriverManager里注册mysql驱动
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }


    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

进入java.sql.DriverManager里,发现mysql吧驱动放入了list里,具体啥时候用看后面。

java.sql.DriverManager

  public static void registerDriver(Driver driver, DriverAction da) throws SQLException {
        if (driver != null) {
		    // registeredDrivers是个list集合
			// private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList();
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
            println("registerDriver: " + driver);
        } else {
            throw new NullPointerException();
        }
    }

进入DriverManager.getConnection(URL, USER, PASSWORD)方法。伪代码如下,发现是建立连接的时候,需要向registeredDrivers找到mysql的驱动,而这个驱动就是上面加载的。也与图1.1相互照应。

java.sql.DriverManager   

   private static Connection getConnection(String url, Properties info, Class<?> caller) throws SQLException {
        if (url == null) {
            throw new SQLException("The url cannot be null", "08001");
        } else {
		   // registeredDrivers集合遍历
            Iterator var5 = registeredDrivers.iterator();

            while(true) {
                while(var5.hasNext()) {
                    DriverInfo aDriver = (DriverInfo)var5.next();
                    if (isDriverAllowed(aDriver.driver, callerCL)) {
                        try {
						  // 使用自己的driver建立链接
                            Connection con = aDriver.driver.connect(url, info);
                            if (con != null) {
                                println("getConnection returning " + aDriver.driver.getClass().getName());
                                return con;
                            }
                        } catch (SQLException var8) {
                            if (reason == null) {
                                reason = var8;
                            }
                        }
                    } else {
                        println("    skipping: " + aDriver.getClass().getName());
                    }
                }
            }
        }
    }

五.druid源码分析

连接池即一组数据库连接的集合,是不是单个连接具有的特征连接池也具有呢?拿druid来说,我们可以进入到DruidDataSource内部,发现其同样继承了DataSource

com.alibaba.druid.pool.DruidDataSource

public class DruidDataSource extends DruidAbstractDataSource
public abstract class DruidAbstractDataSource extends WrapperAdapter implements DruidAbstractDataSourceMBean, DataSource, DataSourceProxy, Serializable

全局搜索getConnection()方法,伪代码如下,连接池的创建是在init()方法

public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        // 初始化
        init();

        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }

进入init

public void init() throws SQLException {
        if (inited) {
            return;
        }

        // 懒加载DruidDriver实例
        DruidDriver.getInstance();

        // 加可重入锁
        final ReentrantLock lock = this.lock;
        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            throw new SQLException("interrupt", e);
        }

        boolean init = false;
        try {
            if (inited) {
                return;
            }

            initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());
             // 创建DataSourceId
            this.id = DruidDriver.createDataSourceId();
            if (this.id > 1) {
                long delta = (this.id - 1) * 100000;
                this.connectionIdSeedUpdater.addAndGet(this, delta);
                this.statementIdSeedUpdater.addAndGet(this, delta);
                this.resultSetIdSeedUpdater.addAndGet(this, delta);
                this.transactionIdSeedUpdater.addAndGet(this, delta);
            }

            
            // 数据库类型判断 mysql oracle
            initCheck();
            // 创建连接数组,最大活跃数
            connections = new DruidConnectionHolder[maxActive];
            evictConnections = new DruidConnectionHolder[maxActive];
            keepAliveConnections = new DruidConnectionHolder[maxActive];

            SQLException connectError = null;

            if (createScheduler != null && asyncInit) {
                for (int i = 0; i < initialSize; ++i) {
                    submitCreateTask(true);
                }
            } else if (!asyncInit) {
                // init connections
                while (poolingCount < initialSize) {
                    try {
                       // 创建连接
                        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
						// 向连接池中添加连接
                        connections[poolingCount++] = holder;
                    } catch (SQLException ex) {
                        LOG.error("init datasource error, url: " + this.getUrl(), ex);
                        if (initExceptionThrow) {
                            connectError = ex;
                            break;
                        } else {
                            Thread.sleep(3000);
                        }
                    }
                }

                if (poolingCount > 0) {
                    poolingPeak = poolingCount;
                    poolingPeakTime = System.currentTimeMillis();
                }
            }

        } catch (SQLException e) {
        } finally {
       
        }
    }

 看到PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection()这个方法,是创建数据库连接的方法,进入createPhysicalConnection

com.alibaba.druid.pool.DruidAbstractDataSource

public PhysicalConnectionInfo createPhysicalConnection() throws SQLException {
    
        try {
            conn = createPhysicalConnection(url, physicalConnectProperties);
            connectedNanos = System.nanoTime();

            if (conn == null) {
                throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass);
            }

            initPhysicalConnection(conn, variables, globalVariables);
            initedNanos = System.nanoTime();

            validateConnection(conn);
            validatedNanos = System.nanoTime();

            setFailContinuous(false);
            setCreateError(null);
        } catch (SQLException ex) {
            setCreateError(ex);
            JdbcUtils.close(conn);
            throw ex;
        } finally {
     
        }

        return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos, variables, globalVariables);
    }

    进入conn = createPhysicalConnection(url, physicalConnectProperties);看到了熟悉的代码

 conn = getDriver().connect(url, info);

public Connection createPhysicalConnection(String url, Properties info) throws SQLException {
        Connection conn;
        if (getProxyFilters().size() == 0) {
            conn = getDriver().connect(url, info);
        } else {
            conn = new FilterChainImpl(this).connection_connect(info);
        }

        createCountUpdater.incrementAndGet(this);

        return conn;
    }

然后循环上面的逻辑,把连接都加入到connections[poolingCount++]数组里

while (poolingCount < initialSize) {
    try {
        PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
        DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
        connections[poolingCount++] = holder;
    } catch (SQLException ex) {
        LOG.error("init datasource error, url: " + this.getUrl(), ex);
        if (initExceptionThrow) {
            connectError = ex;
            break;
        } else {
            Thread.sleep(3000);
        }
    }
}