JDBC (Java DataBase Connectivity)数据库连接池原理解析与实现
一、应用程序直接获取数据库连接的缺点
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。
二、使用数据库连接池优化程序性能
数据库连接池的基本概念
数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.
编写数据库连接池
DBManage 这个类用来读取properties配置文件,创建connection对象,管理connection对象
1 import java.io.IOException; 2 import java.sql.Connection; 3 import java.sql.DriverManager; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 import java.util.Properties; 8 9 import orm.bean.Configuration; 10 import orm.pool.DBConnPool; 11 12 /** 13 * 根据配置信息,维持连接对象的管理 14 * @author Haidnor 15 * 16 */ 17 @SuppressWarnings("all") 18 public class DBManage { 19 /** 20 * 配置信息 21 */ 22 private static Configuration conf; 23 24 /** 25 * 连接池对象 26 */ 27 private static DBConnPool pool = null; 28 /** 29 * 初始化,加载指定的文件 30 */ 31 static { 32 Properties pros = new Properties(); 33 34 try { 35 pros.load(Thread.currentThread().getContextClassLoader().getSystemResourceAsStream("database.properties")); 36 } catch (IOException e) { 37 e.printStackTrace(); 38 } 39 conf = new Configuration(); //读取配置文件,将配置文件内的信息保存在配置对象中 40 conf.setDriver(pros.getProperty("driver")); 41 conf.setUrl(pros.getProperty("url")); 42 conf.setUser(pros.getProperty("user")); 43 conf.setPassword(pros.getProperty("password")); 44 conf.setUsingDB(pros.getProperty("usingDB")); 45 conf.setSrcPath(pros.getProperty("srcPath")); 46 conf.setPoPackage(pros.getProperty("poPackage")); 47 conf.setQueryClass(pros.getProperty("queryClass")); 48 conf.setPoolMinSize(Integer.parseInt(pros.getProperty("poolMinSize"))); 49 conf.setPoolMaxSize(Integer.parseInt(pros.getProperty("poolMaxSize"))); 50 } 51 52 /** 53 * 在连接池中获得Connection对象 54 * @return 55 */ 56 public static Connection getConnetion() { 57 if(pool == null) { 58 pool = new DBConnPool(); 59 } 60 return pool.getConnection(); 61 } 62 63 /** 64 * 创建新的Connection对象 65 * @return 66 */ 67 public static Connection createConnetion() { 68 try { 69 Class.forName(conf.getDriver()); 70 return DriverManager.getConnection(conf.getUrl(),conf.getUser(),conf.getPassword()); //目前直接建立连接,后期加入连接池处理,提高效率 71 } catch (Exception e) { 72 e.printStackTrace(); 73 return null; 74 } 75 } 76 77 /** 78 * 关闭数据库连接ResultSet Statement Connection 79 * @param rs 80 * @param pa 81 * @param conn 82 */ 83 public static void close(ResultSet rs,Statement pa,Connection conn){ 84 if(conn != null){ 85 pool.close(conn); 86 } 87 } 88 90 /** 91 * 返回Configuration对象 92 * @return Configuration对象 93 */ 94 public static Configuration getConf(){ 95 return conf; 96 } 97 }
数据库连接池类DBManage ,将多个Connection对象存放在一个List集合中。在程序初始化前,会自动根据配置文件中设置的连接池对象最小数,往list集合中创建多个Connection对象。
以后使用Connection对象就可以直接从list集合中获取。关闭Connection对象不再使用close()方法,而是将Connection对象再放回到list集合中。
1 import java.sql.Connection; 2 import java.sql.SQLException; 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import orm.core.DBManage; 7 8 /** 9 * 连接池类 10 * @author Haidnor 11 * 12 */ 13 14 public class DBConnPool { 15 16 /** 17 * 连接池对象 18 */ 19 private List<Connection> pool; 20 21 /** 22 * 最大连接数 23 */ 24 private static final int POOL_MAX_SIZE = DBManage.getConf().getPoolMaxSize(); 25 26 /** 27 * 最小连接数 28 */ 29 private static final int POOL_MIN_SIZE = DBManage.getConf().getPoolMinSize(); 30 31 /** 32 * 初始化连接池,使池中的连接数达到最小值 33 */ 34 private void initPool() { 35 if(pool == null) { 36 pool = new ArrayList<Connection>(); 37 } 38 while(pool.size() <= DBConnPool.POOL_MIN_SIZE){ 39 pool.add(DBManage.createConnetion()); 40 } 41 } 42 43 /** 44 * 从连接池中取出一个连接对象 45 * @return 46 */ 47 public synchronized Connection getConnection() { 48 int last_index = pool.size() - 1; 49 Connection connection = pool.get(last_index); 50 pool.remove(last_index); 51 return connection; 52 } 53 54 /** 55 * 将连接放回池中 56 */ 57 public synchronized void close(Connection connection) { 58 if(pool.size() >= POOL_MAX_SIZE){ 59 try { 60 if(connection != null){ 61 connection.close(); 62 } 63 } catch (SQLException e) { 64 e.printStackTrace(); 65 } 66 }else{ 67 pool.add(connection); 68 } 69 } 70 71 72 public DBConnPool(){ 73 initPool(); 74 } 75 76 }
最后建立一个测试类。分别使用数据库连接池Connection对象和直接new Connection对象对数据库某个表进行3000次查询测试
使用连接池耗时952毫秒,不用连接池耗时12742毫秒。
import java.util.List; import orm.core.MySqlQuery; import orm.po.User_yinbiao1; public class Test { //不加连接池耗时 12742毫秒 //加连接池耗时:952毫秒 public static void main(String[] args) { long a = System.currentTimeMillis(); for(int i=0; i<3000; i++){ testQueryRows(); } long b = System.currentTimeMillis(); System.out.println("使用数据库连接池运行时间:" + (b - a) + "毫秒"); } public static void testQueryRows() { List list = new MySqlQuery().queryRows("SELECT * FROM user_yinbiao1", User_yinbiao1.class, new Object[] {}); User_yinbiao1 user = (User_yinbiao1)list.get(2); } }