-
链接池技术
链接池技术的核心思想是:链接复用,经过创建一个数据库链接池以及一套链接使用、分配、管理策略,使得该链接池中的链接能够获得高效、安全的复用,避免了数据库链接频繁创建、关闭的开销。另外,因为对JDBC中的原始链接进行了封装,从而方便了数据库应用对于链接的使用(特别是对于事务处理),提升了获取数据库链接效率,也正是由于这个封装层的存在,隔离了应用的自己的处理逻辑和具体数据库访问逻辑,使应用自己的复用成为可能。链接池主要由三部分组成:链接池的创建、链接池中链接的使用管理、链接池的关闭。
目录
- 接池的创建
- 链接池的管理
- 链接池的关闭
- 链接池的配置
- 链接池的关键技术
- 事务处理
- 并发
- 多数据库服务器
- 链接池应用的实现
链接池的创建
应用程序中创建的链接池实际上是一个静态的。所谓静态链接池是指链接池中的链接在系统初始化时就已分配好,且不能随意关闭链接。Java中提供了不少容器类能够方便的构建链接池,如:Vector、Stack、Servlet、Bean等,经过读取链接属性文件Connections.properties与数据库实例创建链接。在系统初始化时,根据相应的配置建立链接并放置在链接池中,以便须要使用时能从链接池中获取,这样就能够避免链接随意的创建、关闭形成的开销。
链接池的管理
链接池管理策略是链接池机制的核心。当链接池创建后,如何对链接池中的链接进行管理,解决好链接池内链接的分配和释放,对系统的性能有很大的影响。链接的合理分配、释放可提升链接的复用,下降了系统创建新链接的开销,同时也加速了用户的访问速度。下面介绍链接池中链接的分配、释放策略。
链接池的分配、释放策略对于有效复用链接很是重要,咱们采用的方法是一个颇有名的设计模式:Reference Counting(引用记数)。该模式在复用资源方面应用的很是普遍,把该方法运用到对于链接的分配释放上,为每个数据库链接,保留一个引用记数,用来记录该链接的使用者的个数。具体的实现方法是:
当客户请求数据库链接时,首先查看链接池中是否有空闲链接(指当前没有分配出去的链接)。若是存在空闲链接,则把链接分配给客户并做相应处理(即标记该链接为正在使用,引用计数加1)。若是没有空闲链接,则查看当前所开的链接数是否是已经达到maxConn(最大链接数),若是没达到就从新建立一个链接给请求的客户;若是达到就按设定的maxWaitTime(最大等待时间)进行等待,若是等待maxWaitTime后仍没有空闲链接,就抛出无空闲链接的异常给用户。
当客户释放数据库链接时,先判断该链接的引用次数是否超过了规定值,若是超过就删除该链接,并判断当前链接池内总的链接数是否小于minConn(最小链接数),若小于就将链接池充满;若是没超过就将该链接标记为开放状态,可供再次复用。能够看出正是这套策略保证了数据库链接的有效复用,避免频繁地创建、释放链接所带来的系统资源开销。
链接池的关闭
当应用程序退出时,应关闭链接池,此时应把在链接池创建时向数据库申请的链接对象统一归还给数据库(即关闭全部数据库链接),这与链接池的创建正好是一个相反过程。
链接池的配置
数据库链接池中到底要放置多少个链接,才能使系统的性能更佳,用minConn和maxConn来限制。minConn是当应用启动的时候链接池所建立的链接数,若是过大启动将变慢,可是启动后响应更快;若是太小启动加快,可是最初使用的用户将由于链接池中没有足够的链接不可避免的延缓了执行速度。所以应该在开发的过程当中设定较小minConn,而在实际应用的中设定较大minConn。maxConn是链接池中的最大链接数,能够经过反复试验来肯定此饱和点。为此在链接池类ConnectionPool中加入两个方法getActiveSize()和getOpenSize(),ActiveSize 表示某一时间有多少链接正被使用,OpenSize表示链接池中有多少链接被打开,反映了链接池使用的峰值。将这两个值在日志信息中反应出来, minConn的值应该小于平均ActiveSize,而maxConn的值应该在activeSize和OpenSize之间。
链接池的关键技术
事务处理
前面讨论的是关于使用数据库链接进行普通的数据库访问。对于事务处理,状况就变得比较复杂。由于事务自己要求原则性的保证,此时就要求对于数据库的操做符合"All-or-Nothing"原则,即要么所有完成,要么什么都不作。若是简单的采用上述的链接复用的策略,就会发生问题,由于没有办法控制属于同一个事务的多个数据库操做方法的动做,可能这些数据库操做是在多个链接上进行的,而且这些链接可能被其余非事务方法复用。Connection自己具备提供了对于事务的支持,能够经过设置Connection的AutoCommit属性为false,显式的调用 commit或rollback方法来实现。可是要安全、高效的进行链接复用,就必须提供相应的事务支持机制。方法是:采用显式的事务支撑方法,每个事务独占一个链接。这种方法能够大大下降对于事务处理的复杂性,而且又不会妨碍链接的复用。
链接管理服务提供了显式的事务开始、结束(commit或rollback)声明,以及一个事务注册表,用于登记事务发起者和事务使用的链接的对应关系,经过该表,使用事务的部分和链接管理部分就隔离开,由于该表是在运行时根据实际的调用状况动态生成的。事务使用的链接在该事务运行中不能被复用。在实现中,用户标识是经过使用者所在的线程来标识的。后面的全部对于数据库的访问都是经过查找该注册表,使用已经分配的链接来完成的。当事务结束时,从注册表中删除相应表项。
并发
为了使链接管理服务有更大的通用性,咱们必需要考虑到多线程环境,即并发问题。在一个多线程的环境下,必需要保证链接管理自身数据的一致性和链接内部数据的一致性,在这方面Java提供很好的支持(synchronized关键字),这样就很容易使链接管理成为线程安全的。
多数据库服务器
在实际应用中,应用程序经常须要访问多个不一样的数据库。如何经过同一个链接池访问不一样的数据库,是应用程序须要解决的一个核心问题。下面介绍一种解决的途径:
首先,定义一个数据库链接池参数的类,定义了数据库的JDBC驱动程序类名,链接的URL以及用户名口令等等一些信息,该类是用于初始化链接池的参数:
public class ConnectionParam implements Serializable{//各初始化参数的定义}
其次是链接池的工厂类ConnectionFactory,经过该类将一个链接池对象与一个名称对应起来,使用者经过该名称就能够获取指定的链接池对象,实现的主要代码以下:
public class ConnectionFactory{static Hashtable connectionPools = //用来保存数据源名和链接池对象的关系public static DataSource lookup(String dataSourceName) throws
NameNotFoundException{//查找名字为dataSourceName的数据源}public static DataSource bind(Stringname, ConnectionParam param)
throws Exception=/将名字name与使用param初始化的链接池对象绑定
}
public static void unbind(String name) throws NameNotFound
Exception{
//将与名字name绑定的链接池对象删除
}
链接池应用的实现
一个完整的链接池应用包括三个部分:DBConnectionPool类,负责从链接池获取(或建立)链接、将链接返回给链接池、系统关闭时关闭全部链接释放全部资源;DBConnectionManager类,负责装载和注册JDBC驱动、根据属性文件中定义的属性建立DBConnectionPool、跟踪应用程序对链接池的引用等;应用程序对链接池的使用。
本文实现的数据库链接池包括一个管理类DBConnectionManager,负责提供与多个链接池对象(DBConnectionPool类)之间的接口。每个链接池对象管理一组封装过的JDBC链接对象Conn,封装过的JDBC链接对象Conn能够被任意数量的Model层的组件共享。
类Conn 的设计很简单,以下所示:
Class Conn {
Private java. sgl .Connection con; //数据库链接对象
Public Boolean inUse ; //是否被使用
Public long lastAccess; //最近一次释放该链接的时间
Public int useCount; // 被使用次数
}
下面是实现链接池的主要代码:
// 初始化数据库链接池
public static synchronized void FastInitPool()
throws Exception {
try { Class.forName(driver);
for (int i=0; i<size; i++) {
Connection con = createConnection();
if (con!=null) addConnection(con);
} } }
// 向链接池对象中添加数据库链接
private static void addConnection(Connection con) {
if (pool==null||pool1==null) {
pool=new Vector(size);
pool1=new Vector(size); }
pool.addElement(con);
pool1.addElement("false"); }
// 获取数据库链接
public static synchronized Connection getConn()
throws Exception {
Connection conn = null;
try { if (driver = null)
FastInitPool();
// 得到一个可用的(空闲的)链接
for (int i = 0; i < pool.size(); i++) {
conn = (Connection)pool.elementAt(i);
if (pool1.elementAt(i)=="false") {
pool1.set(i,"true");
//System.out.println("从链接池中获取第"+(i+1)+"个空闲链接");
return conn;
}
}
//若是没有可用链接,且已有链接数小于最大链接数限制,则建立并增长一个新链接到链接池
conn = createConnection();
pool.addElement(conn);
pool1.addElement("true");
// System.out.println(" 全部链接都在使用,在链接池中再建立一个新链接");
}
catch (Exception e) {
System.err.println(e.getMessage());
throw new Exception(e.getMessage());
}
return conn; //返回一个有效的新链接
}
public Connection getConnection(String strDriver, String strUrl, String strUserName, String strPassWord)
throws SQLException{
try{ Class.forName(strDriver);
conn = DriverManager.getConnection(strUrl, strUserName, strPassWord); }
return conn; }
摘自百度,方便学习使用。