连接池
在一次查询操作中,60%以上的时间是用于创建数据库连接上了,为了提高执行效率,可以考虑减少创建数据库连接的时间
因为没有办法降低一次创建连接所用时长,所以考虑采用共享的方式降低平均使用成本。典型应用有线程池。
数据库连接池的基本思想是:为数据库连接建立一个“缓冲池”,预先在池中放入一定数量的数据库连接管道,需要时,从池子中取出管道进行使用,操作完毕后,在将管道放入池子中,从而避免了频繁的向数据库申请资源,释放资源带来的性能损耗。同时一般会有一个守护线程定期检查空闲连接是否可用,如果连接已经不稳定则释放连接对象.
public class JdbcService {
//使用单例保证连接池只有一个
private JdbcUtil() { }
private static JdbcUtil ju = new JdbcUtil();
public static JdbcUtil getInstance() {
return ju;
}
private static List<Connection> pool; //具体的连接池容器,集合的最佳选择可以考虑使用阻塞队列
private static final int MAX_SIZE = 10; //连接池的相关参数,实际上还有其他的数据,例如最小连接池、最大空闲时间等
// 实际上还应该有守护线程定期检查连接对象的可用性,如果连接对象不可用则需要释放连接
static { //使用静态块,所以没有线程安全问题
pool = Collections.synchronizedList(new ArrayList<>()); //初始化连接池
try {
Class.forName("com.mysql.cj.jdbc.Driver");
for (int i = 0; i < 3; i++) {
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=UTC",
"root", "123456");
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Connection getConnection() throws SQLException {
Connection conn = null;
if (pool.size() > 0) //如果池中包含连接对象,则直接获取,而不是新建
conn = pool.remove(pool.size() - 1);
else //如果池中没有连接【而且没有到达连接数上限】,则新建对象
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?serverTimezone=UTC", "root", "123456");
// 如果创建个数大于上限,则应该阻塞当前线程或者使用自旋锁再来获取连接
return conn;
}
// 连接使用完毕不是关闭连接,而是释放连接到连接池中。具体开发中可以考虑使用代理模式【JavaSE中23种设计模式之一】
public void realseConnection(Connection conn) throws SQLException {
if (conn != null)
if (pool.size() < MAX_SIZE) //如果连接池中的连接对象没有到达上限,则归还连接到连接池中。如果到达上限则关闭连接
pool.add(conn);
else
conn.close();
}
public void close(ResultSet rs, PreparedStatement ps, Connection conn) throws Exception {
try {
if (rs != null) {
rs.close();
}
} finally {
try {
if (ps != null) {
ps.close();
}
} finally {
if (conn != null) {
realseConnection(conn);
}
}
}
}
public PreparedStatement createPreparedStatement(Connection conn, String sql, Object... params) throws Exception {
PreparedStatement ps = conn.prepareStatement(sql);
if (params != null && params.length > 0) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]); // 不确定数据类型
}
}
return ps;
}
public int executeUpdate(Connection conn, String sql, Object... params) throws Exception {
PreparedStatement ps = createPreparedStatement(conn, sql, params);
return ps.executeUpdate();
}
public ResultSet executeQuery(Connection conn, String sql, Object... params) throws Exception {
PreparedStatement ps = createPreparedStatement(conn, sql, params);
return ps.executeQuery();
}
}
连接池的基本原理
在内部对象池中,维护一定数量的数据库连接,并对外暴露数据库连接的获取和返回方法。如外部使用者可通过getConnection方法获取数据库连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时的连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
连接池同时负责定期检查连接的可用性,并保证最大空闲时间。
连接池的作用
1、资源重用
2、更快的系统响应速度
3、新的资源分配手段
4、统一的连接管理,避免数据库连接泄露