数据库连接池

数据库连接池

​ 所谓的数据库连接池技术,就是用来分配,管理,释放数据库连接的。我们以前用 JDBC 操作数据库的时候,每次操作完都会将连接关闭。数据库连接是极其宝贵的资源,频繁的创建和销毁会极大地降低服务器的性能。因此,我们可以利用池化技术,重复利用数据库资源,避免没必要的开销。

1. 手写数据库连接池

首先,我们来手写一个基本的数据库连接池,加深对数据库连接池原理的理解。

public class MyConnectionPool {
    private static int INIT_SIZE;
    private static int MAX_SIZE;
    private int size;
    private LinkedList<Connection> pool = new LinkedList<>();
    private static final Properties info = new Properties();

    static {
        try (Reader reader = new FileReader("info.properties")) {
            info.load(reader);
            INIT_SIZE = Integer.parseInt(info.getProperty("INIT_SIZE"));
            MAX_SIZE = Integer.parseInt(info.getProperty("MAX_SIZE"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public MyConnectionPool() {
        for (int i = 0; i < INIT_SIZE; i++) {
            pool.add(createConnection());
        }
    }

    public Connection getConnection() {
        if (!pool.isEmpty()) {
            return pool.removeFirst();
        }
        if (size < MAX_SIZE) {
            return createConnection();
        }
        throw new RuntimeException("数据库连接数目已达到最大");
    }

    private Connection createConnection(){
        String url = info.getProperty("url");
        String user = info.getProperty("user");
        String password = info.getProperty("password");
        PoolConnection poolConn = null;
        try {
            Connection conn = DriverManager.getConnection(url, user, password);
            poolConn = new PoolConnection(conn);
            size++;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return poolConn;
    }

    private class PoolConnection extends MyConnectionWrapper {
        public PoolConnection(Connection conn) {
            super(conn);
        }

        @Override
        public void close() throws SQLException {
            if (pool.size() < INIT_SIZE) pool.addLast(this);
            else {
                size--;
                conn.close();
            }
        }
    }
}

当然,优秀的数据库连接池远比这复杂的多。接下来,我们就来看看有哪些优秀的数据库连接池。

2. 优秀的数据库连接池

Sun 公司规定:Java 中所有的数据库连接池都需要实现 DataSource 接口。该接口定义如下:

public interface DataSource extends CommonDataSource, Wrapper {
  	Connection getConnection() throws SQLException;
    
  	Connection getConnection(String username, String password) throws SQLException;
}

其中,定义了两个获取连接的方法。

2.1 DBCP

DBCP 是由 Apache 开发的一个 Java 数据库连接池项目。Tomcat 内置的连接池组件就是 DBCP。单独使用 DBCP需要3个包:common-dbcp.jar,common-pool.jar,common-logging.jar。它预先将数据库连接放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完再放回。单线程,并发量低,性能不好,适用于小型系统。

使用的方式主要有两种,一种是硬编码方式,就是在代码中设置各种参数 (不推荐) 。

BasicDataSource ds = new BasicDataSource();
ds.setInitialSize(3);
ds.setMaxTotal(10);
ds.setMaxIdle(1800);
ds.setUrl("jdbc:mysql://localhost:3306/jdbc_db");
ds.setUsername("root");
ds.setPassword("r00tme");

Connection conn = ds.getConnection();
Assert.assertNotNull(conn);

第二种方式就是配置相应的配置文件 (推荐)。在 dbcp.properties 文件中配置如下信息。

url=jdbc:mysql://localhost:3306/jdbc_db
username=root
password=r00tme
initialSize=3
maxTotal=10
maxIdle=1800
Properties info = new Properties();
Connection conn = null;
try (Reader reader = new FileReader("dbcp.properties")) {
   	info.load(reader);
   	BasicDataSource ds = BasicDataSourceFactory.createDataSource(info);
   	conn = ds.getConnection();
} catch (Exception e) {
   		e.printStackTrace();
}
Assert.assertNotNull(conn);

2.2 C3P0

开源的 JDBC 连接池,支持 JDBC3 规范和 JDBC2 的标准扩展。目前使用它的开源项目有Hibernate、Spring等。单线程,性能较差,适用于小型系统。使用 C3P0 需要 c3p0-0.9.5.2.jar 和 mchange-commons-java-0.2.11.jar。

c3p0 会默认从 src 根目录下读取文件名为 c3p0-config.xml 的文件。在该文件中,我们可以配置我们自己需要的参数,如下所示,更多的参数可以自己查阅相关参数。

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc_db</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">r00tme</property>
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">10</property>
        <property name="maxIdleTime">1800</property>
    </default-config>
</c3p0-config>

然后,我们在 Java 代码中,只要创建 ComboPooledDataSource 对象即可。

ComboPooledDataSource ds = new ComboPooledDataSource();
Connection conn = null;
try {
  	conn = ds.getConnection();
} catch (SQLException e) {
 	e.printStackTrace();
}
Assert.assertNotNull(conn);

2.3 Druid

Druid 是 Java 语言中最好的数据库连接池,Druid 能够提供强大的监控和扩展功能,是一个可用于大数据实时查询和分析的高容错、高性能的开源分布式系统,尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时,Druid 仍能够保持100% 正常运行。主要特色: 为分析监控设计;快速的交互式查询;高可用;可扩展;

我们可以在 druid.properties 配置文件中,配置如下信息。

url=jdbc:mysql://localhost:3306/jdbc_db
username=root
password=r00tme
initialSize=3
maxActive=10
maxWait=1800
Properties info = new Properties();
Connection conn = null;
try(Reader reader = new FileReader("druid.properties")) {
	info.load(reader);
	DataSource ds = DruidDataSourceFactory.createDataSource(info);
	conn = ds.getConnection();
} catch (Exception e) {
  	e.printStackTrace();
}
Assert.assertNotNull(conn);

3. 数据库连接池的优点

数据库连接池主要有以下优点:

  1. 数据库连接得以重用,避免了频繁创建和销毁引起的大量性能开销,同时也增加了系统的稳定性。
  2. 数据库连接池在初始化过程中,往往就已经创建了若干个连接。因此可以提高系统的反应速度。
  3. 如果有多个应用共用一个 DBMS,由于 DBMS 的连接是有限的,因此我们可以通过数据库连接池限制一个应用最大的连接数目,避免某一应用独占所有的连接。
  4. 在较为完善的数据库连接池中,可以设置占用超时,强制回收被占用的连接,从而避免了常规数据库连接操作中可能出现的资源泄露问题。
全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客企业服务