一、为什么我们要用连接池技术?
前面的数据库连接的建立及关闭资源的方法有些缺陷。统舱传统数据库访问方式:一次数据库访问对应一个物理连接,每次操作数据库都要打开、关闭该物理连接, 系统性能严重受损。
解决方案:数据库连接池(Connection Pool)。
系统初始运行时,主动建立足够的连接,组成一个池.每次应用应用程序请求数据库连接时,无需重新打开连接,而是从池中取出已有的连接,使用完后,不再关闭,而是归还。
二、连接池主要由三部分组成:连接池的建立、连接池中连接的使用管理、连接池的关闭。
三、连接池技术的核心思想
连接复用,通过建立一个数据库连接池以及一套连接使用、分配、管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。
1.连接池的建立
在系统初始化时,根据相应的配置创建连接并放置在连接池中,以便需要使用时能从连接池中获取,这样就可以避免连接随意的建立、关闭造成的开销。
2.连接池中连接的使用管理
连接池管理策略是连接池机制的核心。当连接池建立后,如何对连接池中的连接进行管理,解决好连接池内连接的分配和释放,对系统的性能有很大的影响。连接的合理分配、释放可提高连接的复用,降低了系统建立新连接的开销,同时也加速了用户的访问速度。
采用的方法是一个很有名的设计模式:Reference Counting(引用记数)。该模式在复用资源方面应用的非常广泛,把该方法运用到对于连接的分配释放上,为每一个数据库连接,保留一个引用记数,用来记录该连接的使用者的个数。
(1)当客户请求数据库连接时,首先查看连接池中是否有空闲连接(指当前没有分配出去的连接)。如果存在空闲连接,则把连接分配给客户并作相应处理(即标记该连接为正在使用,引用计数加1)。如果没有空闲连接,则查看当前所开的连接数是不是已经达到maxConn(最大连接数),如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的maxWaitTime(最大等待时间)进行等待,如果等待maxWaitTime后仍没有空闲连接,就抛出无空闲连接的异常给用户。
(2)当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就删除该连接,并判断当前连接池内总的连接数是否小于minConn(最小连接数),若小于就将连接池充满;如果没超过就将该连接标记为开放状态,可供再次复用。可以看出正是这套策略保证了数据库连接的有效复用,避免频繁地建立、释放连接所带来的系统资源开销。
3.连接池的关闭
当应用程序退出时,应关闭连接池,此时应把在连接池建立时向数据库申请的连接对象统一归还给数据库(即关闭所有数据库连接),这与连接池的建立正好是一个相反过程。
我们采用DBCP(DataBase connection pool),数据库连接池。DBCP(是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要3个包:commons-dbcp.jar,commons-pool.jar,commons-collections.jar由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。
四、连接池的实现
新建一个java工程并导入相应的包。
配置文件:
1 jdbc.driver=com.mysql.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/csdn 3 jdbc.user=root 4 jdbc.password=123456 5 initsize=1 6 maxactive=1 7 maxwait=5000 8 maxidle=1 9 minidle=1
dbcp的基本配置的介绍
1.initialSize :连接池启动时创建的初始化连接数量(默认值为0)
2.maxActive :连接池中可同时连接的最大的连接数(默认值为8,调整为20,高峰单机器在20并发左右,自己根据应用场景定)
3.maxIdle:连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制(默认为8个,maxIdle不能设置太小,因为假如在高负载的情况下,连接的打开时间比关闭的时间快,会引起连接池中idle的个数 上升超过maxIdle,而造成频繁的连接销毁和创建,类似于jvm参数中的Xmx设置)
4.minIdle:连接池中最小的空闲的连接数,低于这个数量会被创建新的连接(默认为0,调整为5,该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大,因为在机器很空闲的时候,也会创建低于minidle个数的连接,类似于jvm参数中的Xmn设置)
5.maxWait :最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待(默认为无限,调整为60000ms,避免因线程池不够用,而导致请求被无限制挂起)
DBUtil源码如下:
1 package com.daliu.jdbc; 2 3 import java.io.InputStream; 4 import java.sql.Connection; 5 import java.sql.SQLException; 6 import java.util.Properties; 7 8 import org.apache.commons.dbcp.BasicDataSource; 9 10 /** 11 * 使用连接池技术管理数据库连接 12 */ 13 public class DBUtil { 14 15 //数据库连接池 16 private static BasicDataSource dbcp; 17 18 //为不同线程管理连接 19 private static ThreadLocal<Connection> tl; 20 21 //通过配置文件来获取数据库参数 22 static{ 23 try{ 24 Properties prop 25 = new Properties(); 26 27 InputStream is 28 = DBUtil.class.getClassLoader() 29 .getResourceAsStream( 30 "com/daliu/jdbc/db.properties"); 31 32 prop.load(is); 33 is.close(); 34 35 //一、初始化连接池 36 dbcp = new BasicDataSource(); 37 38 39 //设置驱动 (Class.forName()) 40 dbcp.setDriverClassName(prop.getProperty("jdbc.driver")); 41 //设置url 42 dbcp.setUrl(prop.getProperty("jdbc.url")); 43 //设置数据库用户名 44 dbcp.setUsername(prop.getProperty("jdbc.user")); 45 //设置数据库密码 46 dbcp.setPassword(prop.getProperty("jdbc.password")); 47 //初始连接数量 48 dbcp.setInitialSize( 49 Integer.parseInt( 50 prop.getProperty("initsize") 51 ) 52 ); 53 //连接池允许的最大连接数 54 dbcp.setMaxActive( 55 Integer.parseInt( 56 prop.getProperty("maxactive") 57 ) 58 ); 59 //设置最大等待时间 60 dbcp.setMaxWait( 61 Integer.parseInt( 62 prop.getProperty("maxwait") 63 ) 64 ); 65 //设置最小空闲数 66 dbcp.setMinIdle( 67 Integer.parseInt( 68 prop.getProperty("minidle") 69 ) 70 ); 71 //设置最大空闲数 72 dbcp.setMaxIdle( 73 Integer.parseInt( 74 prop.getProperty("maxidle") 75 ) 76 ); 77 //初始化线程本地 78 tl = new ThreadLocal<Connection>(); 79 }catch(Exception e){ 80 e.printStackTrace(); 81 } 82 } 83 84 /** 85 * 获取数据库连接 86 * @return 87 * @throws SQLException 88 */ 89 public static Connection getConnection() throws SQLException{ 90 /* 91 * 通过连接池获取一个空闲连接 92 */ 93 Connection conn 94 = dbcp.getConnection(); 95 tl.set(conn); 96 return conn; 97 } 98 99 100 /** 101 * 关闭数据库连接 102 */ 103 public static void closeConnection(){ 104 try{ 105 Connection conn = tl.get(); 106 if(conn != null){ 107 /* 108 * 通过连接池获取的Connection 109 * 的close()方法实际上并没有将 110 * 连接关闭,而是将该链接归还。 111 */ 112 conn.close(); 113 tl.remove(); 114 } 115 }catch(Exception e){ 116 e.printStackTrace(); 117 } 118 } 119 120 /** 121 * 测试是否连接成功 122 * @param args 123 * @throws SQLException 124 */ 125 public static void main(String[] args) throws SQLException { 126 System.out.println(getConnection()); 127 } 128 }
效果如下: