无论是怎样的应用系统,都无法脱离对资源的管理和使用。而对于持久层而言,资源的合理管理和调度则显得尤为重要。
在大多所应用系统中,80%以上的应用逻辑并不需要特别复杂的数据访问逻辑(可能只是几条简单的Select或者Insert/Update语句)。对于这些占大多数的简单逻辑而言,如果SQL语句和数据库本身的设计不是太糟糕(合理的关联,字段索引以及数据库分区策略),在特定的硬件环境下,我们认为数据库的性能基本稳定,但对于大型业务系统,即使在特定的硬件环境下,并且数据库设计良好也会存在性能低下的问题。那么问题出在哪里呢?
由于每次数据库访问时都获取一个独立的数据库连接,代码在每个操作中都独立获取一个数据库连接进行操作,最后在依次关闭,可以想见,一个业务操作中如果多次重复这样的过程,对于系统来说是多么大的性能开销啊!
如何解决这样的问题呢?我们使用数据库连接池机制(Connection Pool)。
即使对于我们而言,通过JDBC获取连接池实在是件再简单不过的事情,但对于JDBC Driver来说,连接数据库却并非一件轻松差事,数据库连接不仅仅是在应用服务器与数据库服务器之间建立一个Socket Connection(对于Type4的JDBC Driver而言),连接建立之后,应用服务器和数据库服务器之间需要交换若干次数据(验证用户密码、权限等),然后,数据库开始初始化连接会话句柄,记录联机日志,为此连接分配相应的处理进程和系统资源。
系统如此忙碌,如果我们知识简单扔过去两个SQL语句,然后就将次连接抛弃,实在可惜,而数据库连接池技术正是为了解决这个问题。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。
外部使用者可通过getConnection方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器收回,并为下一次使用做好准备。
数据库连接池技术带来下面的优势:
1:资源重用
由于数据库连接池连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)
2:更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3:新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用作大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4:统一的连接管理,避免数据库连接泄露
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄露。
接下来介绍一个连接池的简单实现。
package com.phy.emis.db;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.ResourceBundle;
import java.util.Vector;
public final class Pool {
private static String CONFIG = "db";
private static Vector vConnPool = new Vector();
private static int intMaxConnections = 20;
private static int iRetryTimes = 3;
private static String strDriver;
private static String strUrl;
private static String strUser;
private static String strPWD;
private static String strDataBase;
private static ResourceBundle rb;
public static boolean blnDebug;
private static int intClients = 0;
private static int intNew = 0, intGet = 0, intPut = 0;
static {
try {
rb = ResourceBundle.getBundle(CONFIG);
strDriver = rb.getString("driver"); //驱动程序字符串
strUrl = rb.getString("url"); //url字符串
strUser = rb.getString("user"); //登陆数据库用户名
strPWD = rb.getString("pwd"); //登陆数据库密码
strDataBase = rb.getString("database");
if (rb.getString("debug").equals("true")) {
blnDebug = true;
}
else {
blnDebug = false;
}
intMaxConnections = Integer.parseInt(rb.getString("MaxConnection"));
iRetryTimes = Integer.parseInt(rb.getString("DBRetryTimes"));
}
catch (Exception e) {
System.out.println("不能读取属性文件" + e);
}
}
//新建一个数据库连接
private static synchronized Connection newConnection() {
intNew++;
Connection conn = null;
//System.out.println("new conn:" + intNew);
//初始化数据库驱动程序
try {
Driver cnnDriver = (Driver) Class.forName(strDriver).newInstance();
DriverManager.registerDriver(cnnDriver);
}
catch (Exception e) {
System.out.println("error happened at init driver " + e);
}
//新建一个连接
try {
conn = DriverManager.getConnection(strUrl, strUser, strPWD);
conn.setCatalog(strDataBase);
}
catch (Exception e) {
if (blnDebug) {
System.out.print(e.getMessage());
}
return null;
}
return conn;
}
//从连接池中获得一个连接
public static synchronized Connection getConnection() {
intGet++;
//System.out.println("get conn:" + intGet);
Connection conn = null;
int iIndex = -1;
boolean bClosed;
int iCnt = 0;
//System.out.println("new:"+intNew+" getCNN:"+intGet+" client:"+intClients);
while (iCnt < vConnPool.size()) {
conn = (Connection) vConnPool.elementAt(iCnt);
if (conn == null) { //连接为空,从连接池中移出
vConnPool.removeElementAt(iCnt);
continue;
}
else { //不为空
try {
if (conn.isClosed()) { //连接已经被关闭,从连接池中移出
vConnPool.removeElementAt(iCnt);
continue;
}
else { //有效连接,得到连接在连接池中的位置
iIndex = iCnt;
break;
}
}
catch (Exception e) {
vConnPool.removeElementAt(iCnt);
continue;
}
} //end else
} //end while
if (iIndex != -1) { //已经得到连接,从连接池移出
vConnPool.removeElementAt(iIndex);
intClients++;
}
else {
if (intClients < intMaxConnections) { //没有得到连接:如果连接池未满,新建一个连接
conn = newConnection(); //新建连接
if (conn != null) {
intClients++;
}
}
else {
return null;
}
} //end if
return conn;
}
/** *//*****
* 辅助方法,用于得到数据库重试的次数
* @param none
* @return int 次数
*/
public static int getRetryTimes(){
return iRetryTimes;
}
//将一个连接放入连接池中
public static synchronized void putConnection(Connection conn) {
intPut++;
boolean bClosed;
try {
bClosed = conn.isClosed();
}
catch (Exception e) {
bClosed = true;
}
if (conn != null && !bClosed) {
vConnPool.addElement(conn);
}
if (intClients > 0) {
intClients--;
}
}
//回收所有的连接
protected void finalize() {
try {
for (int i = 0; i < vConnPool.size(); i++) {
Connection conn = (Connection) vConnPool.elementAt(i);
conn.close();
}
}
catch (Exception e) {
}
}
//打印当前数据库的连接池的相关信息,以便调试
public static String prtMsg(String strIn){
String strRet = "<< MSG >> intClients:"+intClients+" intNew:"+
intNew+" Get:"+intGet+" Put:"+intPut + " $$" + strIn ;
if(blnDebug){
System.out.println(strRet);
}
return strRet ;
}
//打印当前数据库的连接池的相关信息,以便调试
public static String prtMsg(){
String strRet = "<< MSG >> intClients:"+intClients+" intNew:"+
intNew+" Get:"+intGet+" Put:"+intPut ;
if(blnDebug){
System.out.println(strRet);
}
return strRet ;
}
public static void processInvaildConn(){
for(int i=0;i<vConnPool.size();i++){
Connection conn = (Connection) vConnPool.get(0);
try {
conn.close();
vConnPool.remove(conn);
//intNew --;
}
catch(Exception e) {
System.out.println("Error at processInvaildConn!"+e);
}
} //end for
System.out.println("From Pool:The connection has been reset by peer!");
intClients = 0;
intNew = 0;
intGet = 0;
intPut = 0;
System.out.println("From Pool:Init Connection!!");
}
}