文章来源:http://www.itnose.net/detail/6039141.html 更多文章:http://www.itnose.net/type/1.html
一、为什么在连接数据库时要使用连接池
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的 性能低下。 数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库联接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。 连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
二、数据库连接池的基本原理
数据库连接池的基本思想就是为数据库连接 建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定 连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量?使用情况,为系统开发?测试及性能调 整提供依据。
三、数据库连接池的工作原理
连接池的工作原理主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、连接池的关闭。
第一、连接池的建立。一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。
第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:
当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。
第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。
四、连接池关键问题分析
1、并发问题
为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为Java语言自身提供了对并发管理的支 持,使用synchronized关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字,如:
public synchronized Connection getConnection()
2、多数据库服务器和多用户
对于大型的企业级应用,常常需要同时连接不同的数据库(如连接Oracle和Sybase)。如何连接不同的数据库呢?我们采用的策略是:设计 一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的url地址()?用户名()?密 码()等信息。如 tx.url=172.21.15.123:5000/tx_it,tx.user=yang,tx.password=yang321。根据资源文件提 供的信息,创建多个连接池类的实例,每一个实例都是一个特定数据库的连接池。连接池管理类实例为每个连接池实例取一个名字,通过不同的名字来管理不同的连 接池。
对于同一个数据库有多个用户使用不同的名称和密码访问的情况,也可以通过资源文件处理,即在资源文件中设置多个具有相同url地址,但具有不同用户名和密码的数据库连接信息。
3、事务处理
我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-ALL-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。
在Java语言中,Connection类本身提供了对事务的支持,可以通过设置Connection的AutoCommit属性为 false,然后显式的调用commit或rollback方法来实现。但要高效的进行Connection复用,就必须提供相应的事务支持机制。可采用 每一个事务独占一个连接来实现,这种方法可以大大降低事务管理的复杂性。
4、连接池的分配与释放
连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
对于连接的管理可使用空闲池。即把已经创建但尚未分配出去的连接按创建时间存放到一个空闲池中。每当用户请求一个连接时,系统首先检查空闲池内 有没有空闲连接。如果有就把建立时间最长(通过容器的顺序存放实现)的那个连接分配给他(实际是先做连接是否有效的判断,如果可用就分配给用户,如不可用 就把这个连接从空闲池删掉,重新检测空闲池是否还有连接);如果没有则检查当前所开连接池是否达到连接池所允许的最大连接数(maxConn),如果没有 达到,就新建一个连接,如果已经达到,就等待一定的时间(timeout)。如果在等待的时间内有连接被释放出来就可以把这个连接分配给等待的用户,如果 等待时间超过预定时间timeout,则返回空值(null)。系统对已经分配出去正在使用的连接只做计数,当使用完后再返还给空闲池。对于空闲连接的状 态,可开辟专门的线程定时检测,这样会花费一定的系统开销,但可以保证较快的响应速度。也可采取不开辟专门线程,只是在分配前检测的方法。
5、连接池的配置与维护
连接池中到底应该放置多少连接,才能使系统的性能最佳?系统可采取设置最小连接数(minConn)和最大连接数(maxConn)来控制连接 池中的连接。最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很 快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连 接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过反复测试,找到最佳点。
如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。
五、连接池实现代码(java)
package book.util; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.Driver; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Vector; public class Pool { public static void main(String[] args) { Pool pool = new Pool("com.microsoft.sqlserver.jdbc.SQLServerDriver","jdbc:sqlserver://localhost:1433;DataBaseName=Book","sa","aaaaaa"); try { pool.createConnections(4); } catch (SQLException e) { e.printStackTrace(); } Connection conn = pool.getConnection(); try { String sql = "select * from allbook"; PreparedStatement ps; ps = conn.prepareStatement(sql); ResultSet rs=ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString("BOOKNAME")); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ pool.returnConnection(conn); } //long startTime=System.currentTimeMillis(); //long endTime=System.currentTimeMillis(); //System.out.println("程序运行时间: "+(endTime-startTime)+"ms"); } private String jdbcDriver = "";//数据库驱动 private String dbUrl = "";//数据库url private String dbUsername = "";//数据库用户名 private String dbPassword = "";//数据库密码 private String testTable = ""; private int initialConnectionsNum = 10;//连接池初始连接数 private int maxConnectionsNum = 50;//连接池最大连接数 private int incrementalConnections = 5;//每次动态添加的连接数 private Vector<PooledConnection> connections = null;//向量,存放连接池中的连接,初始为空 /*无参构造函数*/ public Pool() {} /*带参数的构造函数 * 初始化数据库驱动、数据库url、数据库用户名、数据库密码、测试表 * */ public Pool(String driver, String url, String name, String pass) { this.jdbcDriver = driver; this.dbUrl = url; this.dbUsername = name; this.dbPassword = pass; //this.testTable = table; try { this.createPool(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /*函数,创建连接池*/ public synchronized void createPool() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { /*确保连接池为创建,如果已经创建,则保存连接的向量不为空 * */ if (this.connections != null) { return ; } //驱动器实例化 Driver driver = (Driver)(Class.forName(this.jdbcDriver).newInstance()); //注册驱动器 DriverManager.registerDriver(driver); //创建保存连接的向量 this.connections = new Vector<PooledConnection>(); //创建数据库连接 this.createConnections(this.initialConnectionsNum); } /*函数,创建数据库连接 * */ private void createConnections (int num) throws SQLException { /*循环创建连接 * 需要首先检查当前连接数是否已经超出连接池最大连接数 * */ for (int i = 0; i < num; ++i) { //检查 if (this.connections.size() >= this.maxConnectionsNum) { return; } //创建连接 this.connections.addElement (new PooledConnection(newConnection())); } } /*函数,创建一个数据库连接*/ private Connection newConnection() throws SQLException { /*创建连接*/ Connection con = DriverManager.getConnection(this.dbUrl, this.dbUsername, this.dbPassword); /*如果是第一次创建连接,则检查所连接的数据库的允许最大连接数是否小于 * 我们所设定的最大连接数*/ if (this.connections.size() == 0) { DatabaseMetaData metadata = con.getMetaData(); //得到数据库最大连接数 int dbMaxConnectionsNum = metadata.getMaxConnections(); //如果数据库最大连接数更小,则更改我们所设定的连接池最大连接数 if (dbMaxConnectionsNum > 0 && this.maxConnectionsNum > dbMaxConnectionsNum) { this.maxConnectionsNum = dbMaxConnectionsNum; } } return con; } /*函数,得到一个可用连接 * */ public synchronized Connection getConnection () { Connection con = null; /*检查连接池是否已经建立*/ if (this.connections == null) { return con; } //得到一个可用连接 try { con = this.getFreeConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果未找到合适连接,循环等待、查找,知道找到合适连接 while(con == null) { this.wait(30); try { con = this.getFreeConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return con; } /*函数,得到一个可用连接*/ private Connection getFreeConnection() throws SQLException { Connection con = null; //查找一个可用连接 con = this.findFreeConnection(); //如果未找到可用连接,就建立一些新的连接,再次查找 if (con == null) { this.createConnections(this.incrementalConnections); //再次查找 con = this.findFreeConnection(); } return con; } /*函数,从现有连接中查找一个可用连接 * 在现有的连接中(向量connections中)找到一个空闲连接, * 并测试这个链接是否可用,若不可用则重新建立连接,替换原来的连接*/ private Connection findFreeConnection () throws SQLException { Connection con = null; for (int i = 0; i < this.connections.size(); ++i) { PooledConnection pol = (PooledConnection)this.connections.get(i); if (!pol.isBusy()) { /*如果此链接未被使用,则返回这个连接并,设置正在使用标志*/ con = pol.getCon(); pol.setBusy(true); /*测试连接是否可用*/ if (!this.testCon(con)) { con = this.newConnection(); pol.setCon(con); } break; } } return con; } /*函数,测试连接是否可用 * */ private boolean testCon (Connection con) { boolean useable = true; try { Statement st = con.createStatement(); ResultSet rs = st.executeQuery("select count(*) from " + this.testTable); rs.next(); } catch(SQLException e) { /*上面抛出异常,连接不可用,关闭*/ useable = false; this.closeConnection(con); } return useable; } /*函数,将使用完毕的连接放回连接池中 * */ public void returnConnection(Connection con) { /*确保连接池存在*/ if (this.connections == null) { return ; } for (int i = 0; i < this.connections.size(); ++i) { PooledConnection pool = this.connections.get(i); //找到相应连接,设置正在使用标志为false if (con == pool.getCon()) { pool.setBusy(false); } } } /*函数,刷新连接池中的连接*/ public synchronized void refreshConneciontPool () throws SQLException { /*确保连接池存在*/ if (this.connections == null) { return ; } for (int i = 0; i < this.connections.size(); ++i) { PooledConnection pool = this.connections.get(i); if (pool.isBusy()) { this.wait(5000); } this.closeConnection(pool.getCon()); pool.setCon(this.newConnection()); pool.setBusy(false); } } /*函数,关闭连接池*/ public void closeConnectionPool() { /*确保连接池存在*/ if (this.connections == null) { return ; } for (int i = 0; i < this.connections.size(); ++i) { PooledConnection pool = this.connections.get(i); if (pool.isBusy()) { this.wait(5000); } this.closeConnection(pool.getCon()); this.connections.remove(i); } this.connections = null; } /*函数,暂时无可用连接,进入等待队列等待m秒,再试 * */ private void wait(int mSecond) { try { Thread.sleep(mSecond); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @return the jdbcDriver */ public String getJdbcDriver() { return jdbcDriver; } /** * @param jdbcDriver the jdbcDriver to set */ public void setJdbcDriver(String jdbcDriver) { this.jdbcDriver = jdbcDriver; } /** * @return the dbUrl */ public String getDbUrl() { return dbUrl; } /** * @param dbUrl the dbUrl to set */ public void setDbUrl(String dbUrl) { this.dbUrl = dbUrl; } /** * @return the dbUsername */ public String getDbUsername() { return dbUsername; } /** * @param dbUsername the dbUsername to set */ public void setDbUsername(String dbUsername) { this.dbUsername = dbUsername; } /** * @return the dbPassword */ public String getDbPassword() { return dbPassword; } /** * @param dbPassword the dbPassword to set */ public void setDbPassword(String dbPassword) { this.dbPassword = dbPassword; } /** * @return the testTable */ public String getTestTable() { return testTable; } /** * @param testTable the testTable to set */ public void setTestTable(String testTable) { this.testTable = testTable; } /** * @return the initialConnectionsNum */ public int getInitialConnectionsNum() { return initialConnectionsNum; } /** * @param initialConnectionsNum the initialConnectionsNum to set */ public void setInitialConnectionsNum(int initialConnectionsNum) { this.initialConnectionsNum = initialConnectionsNum; } /** * @return the maxConnectionsNum */ public int getMaxConnectionsNum() { return maxConnectionsNum; } /** * @param maxConnectionsNum the maxConnectionsNum to set */ public void setMaxConnectionsNum(int maxConnectionsNum) { this.maxConnectionsNum = maxConnectionsNum; } /** * @return the incrementalConnections */ public int getIncrementalConnections() { return incrementalConnections; } /** * @param incrementalConnections the incrementalConnections to set */ public void setIncrementalConnections(int incrementalConnections) { this.incrementalConnections = incrementalConnections; } /** * @return the connections */ public Vector<PooledConnection> getConnections() { return connections; } /** * @param connections the connections to set */ public void setConnections(Vector<PooledConnection> connections) { this.connections = connections; } /*函数,连接使用完毕,关闭连接*/ private void closeConnection (Connection con) { try { con.close(); } catch(SQLException e) { e.printStackTrace(); } } /*内部使用的保存数据库连接的类 * 两个成员变量:连接、是否正在使用*/ class PooledConnection { private Connection con = null;//连接 private boolean busy = false;//是否正在使用,默认为非 /*构造函数*/ public PooledConnection(Connection con) { this.con = con; } /** * @return the con */ public Connection getCon() { return con; } /** * @param con the con to set */ public void setCon(Connection con) { this.con = con; } /** * @return the busy */ public boolean isBusy() { return busy; } /** * @param busy the busy to set */ public void setBusy(boolean busy) { this.busy = busy; } } }
相关推荐
Java数据库连接池的原理与应用 在Java开发领域,数据库连接池(Database Connection Pool,简称DBCP)是一种提高数据库访问性能、降低资源消耗的重要技术。它通过预先建立一定数量的数据库连接,存储在一个“池”中...
本文探讨了Java数据库连接池的运行原理,探究了连接池的工作原理、程序实现和优点。连接池是一种技术,用于解决数据库连接的频繁分配和释放问题,提高系统性能。 数据库连接池的原理 数据库连接池的原理是建立一个...
本文将详细介绍如何在 Java 中实现一个完整的数据库连接池,并探讨其核心概念、工作原理以及如何根据需求进行配置。 首先,我们需要理解数据库连接池的基本概念。数据库连接池是一种对象池设计模式的应用,它维护了...
Java JDBC 数据库连接池总结 Java 语言中,JDBC(Java DataBase Connection)是应用程序与数据库沟通的桥梁。在 Web 应用开发的早期,主要使用的技术是 CGIASPPHP 等。之后,Sun 公司推出了基于 Java 语言的 ...
Java数据库连接池是Java开发中一个非常重要的技术,它主要用于管理数据库连接,提高数据库操作...通过深入学习《最新修改java数据库连接池原理与应用(陈迁小组)》这份资料,你将对数据库连接池有更全面、深入的理解。
描述:本篇将详细介绍Java数据库连接池的概念,代码实现以及其在实际项目中的应用。数据库连接池是Java开发中优化数据库访问效率的关键技术,通过复用预创建的数据库连接,避免频繁创建和销毁连接带来的性能开销。 ...
本篇文章将深入解析一个自定义的JAVA数据库连接池类,帮助开发者更好地理解和运用这一关键技术。 首先,连接池的基本思想是预先创建一定数量的数据库连接,并存储在一个集合(如Vector)中,供应用程序按需获取和...
总结,这个轻量级的Java数据库连接池提供了简单而有效的数据库连接管理方案,对于小型项目或学习数据库连接池原理是非常有价值的。通过理解其工作原理和使用方法,开发者可以更好地优化自己的应用程序,提高系统的...
### Java中数据库连接池原理机制详解 #### 一、引言 在现代软件开发中,尤其是在基于Java的应用程序中,数据库连接池技术是一项至关重要的技术。它能够显著提高应用程序访问数据库的效率,减少资源消耗,并简化...
本文将深入探讨Java数据库连接池的原理、使用以及示例。 首先,理解数据库连接池的工作机制至关重要。数据库连接池在初始化时会创建一定数量的数据库连接,这些连接存储在内存中,形成一个“池”。应用程序在需要时...
通过以上步骤,你可以全面了解JDBC数据库连接池的实现原理,以及如何在实际项目中有效地使用和配置它。在实践中,结合Oracle数据库的特性和JDBC API,可以更好地优化数据库操作,提升系统的稳定性和效率。
本资源提供了一份完整的Java数据库连接池的源代码,具有简单易用且注释详尽的特点,适合初学者学习和开发者参考。 在Java中,数据库连接池主要有以下几个关键知识点: 1. **连接池概念**:数据库连接池是一组预先...
在实际开发中,虽然手写数据库连接池可以加深对数据库连接池原理的理解,但更常见的情况是使用成熟的连接池实现,如C3P0、Apache DBCP、HikariCP等,它们经过大量优化,提供了更好的性能和稳定性。 总的来说,理解...
本文将深入探讨如何使用Java代码来实现一个简单的数据库连接池,并解释其核心概念和工作原理。 连接池的基本思想是维护一组预初始化的数据库连接,当应用程序需要时,可以从池中获取一个连接,使用完毕后,再归还回...