实现一个简易的数据库连接池

数据库连接池的作用:

1. 资源重用

当多个用户频繁的去对数据库进行读写操作时,会不间断的创建Connection,在数据库开始读写数据之前,把资源过多的分配给创建连接释放连接上,这笔开销得不偿失.数据库连接池的对连接Connection的资源回收机制对此做出了优化

2. 更快的系统响应速度

数据库连接池一旦初始化,用户获取的Connection不再创建新的,而是从现有的容器里面取,使得直接利用成为可能,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间

3. 新的资源分配手段

对于多应用共享同一数据库的系统而言 ,某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源

4. 统一的连接管理,避免数据库连接泄漏

数据库连接池提供连接超时设定,超时后会重试,针对用户获取到的不健康的Connection,同样会重新分配新的连接,从而避免了常规数据库连接操作中可能出现的资源泄漏

实现一个简单的数据库连接池

目标:

  • 要实现的数据库连接池,统一注册驱动(替换每一次连接数据库都要注册驱动的时代)
  • 用户想对数据库进行进一步的操作,需要在数据库连接池中获取连接Connection
  • 用户对队数据库读写完毕之后, 连接池回收当前的Connection

两个容器

数据库连接池 = 空闲连接池 + 工作连接池

三个重要的参数

  • 最大连接数
    • 最大连接数是对Connection总数的限制 一般是((核心数 * 2) + 有效磁盘数)
  • 空闲连接数
    • 空闲连接数 表示当前的空闲连接池中的Connection的数量,我们给他规定最大值和最小值
    • 当前值 < 最大连接数 表示用户有机会获取连接
      • 如果 空闲连接池的size>0 直接获取连接
      • 否则 创建一个新的Connection给用户
    • 当前值 >= 最大连接数 Connection的创建达到了上限,用户只能等待重试
  • 工作连接数
    • 工作连接数 表示 当前工作连接池中的Connection的数量

获取连接经历什么?

  1. 空闲连接池中的弹出一个Connection
  2. 把当前的Connection加入到工作连接池

连接是如何被回收的?

  1. 如果空闲连接池未满.直接添加进去
  2. 把工作连接池中相应的连接移除
  3. 如果空闲连接池满了.直接close()掉
  4. 把工作连接池中相应的连接移除

配置文件读取工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class propertiesUtil {

private static Properties configObj = new Properties();

static{
// 使用类加载器读取的文件要放在src下
InputStream connectionPool = Thread.currentThread().getContextClassLoader().getResourceAsStream("pool.properties");
InputStreamReader inputStreamReader = new InputStreamReader(connectionPool);
try {
configObj.load(inputStreamReader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getValue(String key){
return configObj.getProperty(key);
}
}

定义接口,规范我们的连接池的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface myConnectionPool  {
/**
* 获取连接
* @return
*/
public Connection getConnection();

/**
* 释放连接
* @param connection
*/
public void releaseConnection(Connection connection);
}

具体的实现类实现myConnectionPool接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public class ConnectionPool implements IConnection {
// 线程安全的两个容器,分别存放空闲线程数和活动的线程数
private List<Connection> freeConnection = new CopyOnWriteArrayList<>();
private List<Connection> activeConnection = new CopyOnWriteArrayList<>();
// 原子类 标记的是 空闲池的存放的连接数
private AtomicInteger atomicInteger;

public ConnectionPool() {

this.atomicInteger = new AtomicInteger(0);
// 初始化空闲连接池
init();
}

// 初始空闲连接池
public void init() {
// 获取连接数,给freeConnection 池添加指定数量的连接数
for (int i = 0; i < Integer.valueOf(propertiesUtil.getValue("initConnections")); i++) {
// 创建连接
Connection connection = newConnection();
if (null != connection) {
// 添加到容器
freeConnection.add(connection);
}
}
}

// 获取连接
@Override
public synchronized Connection getConnection() {
Connection connection=null;
// 判断是否达到了最大连接数--> 决定给用户连接还是让他等待
if (atomicInteger.get()<Integer.valueOf(propertiesUtil.getValue("maxActiveConnetions"))){
// 当前小于最大的连接数,直接给当前的用户连接
if (freeConnection.size()>0){ // 空闲线程里面有直接从空闲线程里面取
connection = freeConnection.remove(0);
}else{ // 空闲线程里面没有,直接创建一个新的连接
connection = newConnection();
}
// 判断连接是否可用
if(isAvailable(connection)){
// 添加到一个活动线程里面
activeConnection.add(connection);
}else{ // 如果连接不可用,递归
// 如果连接不可用的话,说明有一次newConnection()失败了,我们得 atomicInteger.decrementAndGet(); 把newConnection()里面的原子增加去掉
atomicInteger.decrementAndGet();
connection = getConnection();
}
}else{
// 等待
try {
wait(Integer.valueOf(propertiesUtil.getValue("connTimeOut")));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return connection;
}

// 释放本次连接 ; 把本次连接从活动池 转移到 空闲池
@Override
public synchronized void releaseConnection(Connection connection) {
// 判断连接是否可用
if(isAvailable(connection)){ // 可用
// 回收
// 判断空闲池是否满了
if(freeConnection.size()<Integer.valueOf(propertiesUtil.getValue("maxConnections"))){
// 未满
freeConnection.add(connection);
}else{
// 满了
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 移除出去当前的这个连接
activeConnection.remove(connection);
atomicInteger.decrementAndGet();
// 现在可能有连接正在等待,既然这里释放了,那么就唤醒全部等待的线程
notifyAll();
}else{ // 不可用
throw new RuntimeException("连接回收异常");
}
}


// 创建新的连接
public Connection newConnection() {
try {
// 注册驱动
Class.forName(propertiesUtil.getValue("driverName"));
// 获取连接
Connection connection = DriverManager.getConnection(
propertiesUtil.getValue("url"),
propertiesUtil.getValue("userName"),
propertiesUtil.getValue("password"));
// 原子增加
atomicInteger.addAndGet(1);
return connection;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

// 判断连接是否可用
public boolean isAvailable(Connection connection){
try {
if (null==connection||connection.isClosed()){
return false;
}
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 数据库相关
driverName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/text
userName=root
password=root

# 空闲池的 最小数
minConnections=1
# 空闲池的 最大数
maxConnections=10
# 初始化的连接数
initConnections=5

# 本次连接超时时间(重试时间)
connTimeOut=1000
# 最大的连接数
maxActiveConnetions=10