javaweb学习总结(三十九)——数据库连接池
一、应用程序直接获取数据库连接的缺点
用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天 10 万访问量,数据库服务器就需要创建 10 万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:
二、使用数据库连接池优化程序性能
2.1、数据库连接池的基本概念
数据库连接是一种关键的有限的昂贵的资源, 这一点在多用户的网页应用程序中体现的尤为突出. 对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性, 影响到程序的性能指标. 数据库连接池正式针对这个问题提出来的.数据库连接池负责分配, 管理和释放数据库连接, 它允许应用程序重复使用一个现有的数据库连接, 而不是重新建立一个。如下图所示:
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的. 无论这些数据库连接是否被使用, 连接池都将一直保证至少拥有这么多的连接数量. 连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数, 当应用程序向连接池请求的连接数超过最大连接数量时, 这些请求将被加入到等待队列中.
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
- 最小连接数: 是连接池一直保持的数据库连接, 所以如果应用程序对数据库连接的使用量不大, 将会有大量的数据库连接资源被浪费.
- 最大连接数: 是连接池能申请的最大连接数, 如果数据库连接请求超过次数, 后面的数据库连接请求将被加入到等待队列中, 这会影响以后的数据库操作
- 如果最小连接数与最大连接数相差很大: 那么最先连接请求将会获利, 之后超过最小连接数量的连接请求等价于建立一个新的数据库连接. 不过, 这些大于最小连接数的数据库连接在使用完不会马上被释放, 他将被放到连接池中等待重复使用或是空间超时后被释放.
2.2、编写数据库连接池
编写连接池需实现 java.sql.DataSource 接口。DataSource 接口中定义了两个重载的 getConnection 方法:
- Connection getConnection()
- Connection getConnection(String username, String password)
实现 DataSource 接口,并实现连接池功能的步骤:
- 在 DataSource 构造函数中批量创建与数据库的连接,并把创建的连接加入 LinkedList 对象中。
- 实现 getConnection 方法,让 getConnection 方法每次调用时,从 LinkedList 中取一个 Connection 返回给用户。
- 当用户使用完 Connection,调用 Connection.close() 方法时,Collection 对象应保证将自己返回到 LinkedList 中, 而不要把 conn 还给数据库。Collection 保证将自己返回到 LinkedList 中是此处编程的难点。
数据库连接池核心代码
使用动态代理技术构建连接池中的 connection
1 proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
2 .getClassLoader(), conn.getClass().getInterfaces(),
3 new InvocationHandler() {
4 //此处为内部类,当 close 方法被调用时将 conn 还回池中, 其它方法直接执行
5 public Object invoke(Object proxy, Method method,
6 Object[] args) throws Throwable {
7 if (method.getName().equals("close")) {
8 pool.addLast(conn);
9 return null;
10 }
11 return method.invoke(conn, args);
12 }
13 });
数据库连接池编写范例:
1 package me.gacl.demo;
2
3 import java.io.InputStream;
4 import java.io.PrintWriter;
5 import java.lang.reflect.InvocationHandler;
6 import java.lang.reflect.Method;
7 import java.lang.reflect.Proxy;
8 import java.sql.Connection;
9 import java.sql.DriverManager;
10 import java.sql.SQLException;
11 import java.util.LinkedList;
12 import java.util.Properties;
13 import javax.sql.DataSource;
14
15 /**
16 * @ClassName: JdbcPool
17 * @Description: 编写数据库连接池
18 * @author: 孤傲苍狼
19 * @date: 2014-9-30 下午 11:07:23
20 *
21 */
22 public class JdbcPool implements DataSource{
23
24 /**
25 * @Field: listConnections
26 * 使用 LinkedList 集合来存放数据库链接,
27 * 由于要频繁读写 List 集合,所以这里使用 LinkedList 存储数据库连接比较合适
28 */
29 private static LinkedList<Connection> listConnections = new LinkedList<Connection>();
30
31 static{
32 //在静态代码块中加载 db.properties 数据库配置文件
33 InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
34 Properties prop = new Properties();
35 try {
36 prop.load(in);
37 String driver = prop.getProperty("driver");
38 String url = prop.getProperty("url");
39 String username = prop.getProperty("username");
40 String password = prop.getProperty("password");
41 //数据库连接池的初始化连接数大小
42 int jdbcPoolInitSize =Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
43 //加载数据库驱动
44 Class.forName(driver);
45 for (int i = 0; i < jdbcPoolInitSize; i++) {
46 Connection conn = DriverManager.getConnection(url, username, password);
47 System.out.println("获取到了链接" + conn);
48 //将获取到的数据库连接加入到 listConnections 集合中,listConnections 集合此时就是一个存放了数据库连接的连接池
49 listConnections.add(conn);
50 }
51
52 } catch (Exception e) {
53 throw new ExceptionInInitializerError(e);
54 }
55 }
56
57 @Override
58 public PrintWriter getLogWriter() throws SQLException {
59 // TODO Auto-generated method stub
60 return null;
61 }
62
63 @Override
64 public void setLogWriter(PrintWriter out) throws SQLException {
65 // TODO Auto-generated method stub
66
67 }
68
69 @Override
70 public void setLoginTimeout(int seconds) throws SQLException {
71 // TODO Auto-generated method stub
72
73 }
74
75 @Override
76 public int getLoginTimeout() throws SQLException {
77 // TODO Auto-generated method stub
78 return 0;
79 }
80
81 @Override
82 public <T> T unwrap(Class<T> iface) throws SQLException {
83 // TODO Auto-generated method stub
84 return null;
85 }
86
87 @Override
88 public boolean isWrapperFor(Class<?> iface) throws SQLException {
89 // TODO Auto-generated method stub
90 return false;
91 }
92
93 /* 获取数据库连接
94 * @see javax.sql.DataSource#getConnection()
95 */
96 @Override
97 public Connection getConnection() throws SQLException {
98 //如果数据库连接池中的连接对象的个数大于 0