数据库连接池
概述
数据库连接池是负责分配,管理和释放数据库连接的:它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个:释放空闲时间超过最大空闲时间的数据库来避免
因为没有释放数据路连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
• 为什么要使用连接池
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。
数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库连接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。
连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。
• Java中常用的数据库连接池有
- DBCP
- C3P0
- BoneCP
- Proxool
- DDConnectionBroker
- DBPool
- XAPool
- Primrose
- SmartPool
- MiniConnectionPoolManager
- Druid
• 创建连接的实例
package demo1; import java.sql.Connection; public interface IDBConnection { //初始化连接 void init(); //获取连接 Connection getConnection(); //关闭连接 void close(Connection conn); }
• 实现数据库连接的类Util类
public class Util { private static final String DRIVER="com.mysql.jdbc.Driver"; private static final String URL="jdbc:mysql://127.0.0.1:3306/procedure3"; private static final String USER="root"; private static final String PASSWORD=""; public static Connection getConnection(){ Connection conn=null; try { Class.forName(DRIVER); conn= DriverManager.getConnection(URL,USER,PASSWORD); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; }
• 连接池类MySQLConnectionPool.java:
public class MySQLConnectionPool implements IDBCConnection { //最小连接数 private static final int minConnection=1; //最大连接数 private static final int maxConnection=10; //存储连接的集合 private static final LinkedList<Connection> pools=new LinkedList<Connection>(); private MySQLConnection mySQLConnection=new MySQLConnection(); /** * 实例化并将连接放入连接池 */ public void init() { Connection conn=null; for (int i=0;i<minConnection;i++){ conn=mySQLConnection.buildConnection(); pools.add(conn); } } /** * 当我们需要使用连接时,连接池中没有连接 * 我们创建一个新的连接,如果连接池中有连接,则从连接池中取出连接 */ public synchronized Connection getConnection() { Connection conn=null; if (pools.size()==0){ conn=mySQLConnection.buildConnection(); }else{ conn=pools.remove(0); } return conn; } /** * 连接使用完毕后再返回到连接池中 * @param conn */ public void close(Connection conn) { if (pools.size()<maxConnection){ pools.add(conn); } } }
• 连接池使用测试类TestCustonConnectionPool.java
public class TestCustonConnectionPool { public static void main(String[] args) { IDBCConnection dbConn=new MySQLConnectionPool(); //初始化连接池 dbConn.init(); long start =System.currentTimeMillis(); for (int i=0;i<100;i++){ //调用连接池中的方法 Connection conn=dbConn.getConnection(); String sql="SELECT * FROM EMPLOYEES"; try { PreparedStatement pstm=conn.prepareStatement(sql); ResultSet rs=pstm.executeQuery(); System.out.println(rs); pstm.close(); //使用完将连接池返回到池中 dbConn.close(conn); } catch (SQLException e) { e.printStackTrace(); } } long end=System.currentTimeMillis(); System.out.println(end-start+"ms"); } }
• 连接池需要注意的点
1、并发问题
为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)lock(C#)关键字即可确保线程是同步的。使用方法可以参考,相关文献。
2、事务处理
我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。
我们知道当2个线程共用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使Connection类提供了相应的事务支持,可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有2个线程都在进行事务操作而引起的。为此我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。
3、连接池的分配与释放
连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。
对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他(如何能找到最合适的连接文章将在关键议题中指出);如果没有就抛出一个异常给用户,List中连接是否可以被分配由一个线程来专门管理捎后我会介绍这个线程的具体实现。
心得:
要努力,但是不要着急,繁花锦簇,硕果累累,都需要过程的,加油!