数据库连接池
数据库连接池
所谓的数据库连接池技术,就是用来分配,管理,释放数据库连接的。我们以前用 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. 数据库连接池的优点
数据库连接池主要有以下优点:
- 数据库连接得以重用,避免了频繁创建和销毁引起的大量性能开销,同时也增加了系统的稳定性。
- 数据库连接池在初始化过程中,往往就已经创建了若干个连接。因此可以提高系统的反应速度。
- 如果有多个应用共用一个 DBMS,由于 DBMS 的连接是有限的,因此我们可以通过数据库连接池限制一个应用最大的连接数目,避免某一应用独占所有的连接。
- 在较为完善的数据库连接池中,可以设置占用超时,强制回收被占用的连接,从而避免了常规数据库连接操作中可能出现的资源泄露问题。