Oracle之数据库连接池

连接池出现的背景:

数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。

数据量少的情况:

每一次 web 请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费 0.05s~1s 的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。如下面的用户查询案例,如果同时有 1000 人访问,就会不断的有数据库连接、断开操作:

 

 

 “数据库连接”是一种稀缺的资源,为了保障网站的正常使用,应该对其进行妥善管理。其实我们查询完数据库后,如果不关闭连接,而是暂时存放起来,当别人使用时,把这个连接给他们使用。就避免了一次建立数据库连接和断开的操作时间消耗。原理如下:

我们自己尝试开发一个连接池,来为上面的查询业务提供数据库连接服务:

       ①   编写 class 实现 DataSource 接口

       ②   在 class 构造器一次性创建 10 个连接,将连接保存 LinkedList 中

       ③   实现 getConnection  从 LinkedList 中返回一个连接

       ④   提供将连接放回连接池中方法

1、连接池代码        

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
public class MySelfDataSource implements DataSource {  //链表 --- 实现栈结构,链表结构方便插入跟删除操作
        private LinkedList<Connection> dataSources = new LinkedList<Connection>();
 
        //初始化连接数量
        public MySelfDataSource() {
               //一次性创建10个连接
               for(int i = 0; i < 10; i++) {
                      try {
                         //1、装载Oracle驱动对象,作用是:1、装载。将字节码读入内存,并产生一个与之对应的java.lang.Class类对象2、连接。这一步会验证字节码,为static变量分配内存,并赋默认值(0或null),并可选的解析符号引用(这里不理解没关系)3、初始化。为类的static变量赋初始值,假如有static int a = 1;这个将a赋值为1的操作就是这个时候做的。除此之外,还要调用类的static块。(这一步是要点)
                         Class.forName("oracle.jdbc.OracleDriver");// 或者使用DriverManager.registerDriver(new OracleDriver());                               //2、通过JDBC建立数据库连接
                         Connection con =DriverManager.getConnection(
                            "jdbc:oracle:thin:@10.148.3.20:1521:tgtest", "saa", "123");
                         //3、将连接加入连接池中
                         dataSources.add(con);
                      } catch (Exception e) {
                         e.printStackTrace();
                      }
               }
        }
 
        @Override
        publicConnection getConnection() throws SQLException {
               //取出连接池中一个连接
               finalConnection conn = dataSources.removeFirst(); // 删除第一个连接返回
               return conn;
        }
 
        //将连接放回连接池
        publicvoid releaseConnection(Connection conn) {
               dataSources.add(conn);
               }
 }

  2.使用连接池重构我们的用户查询函数  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//查询所有用户
   Public void FindAllUsers(){
          //1、使用连接池建立数据库连接
          MyDataSource dataSource = new MyDataSource();
          Connection conn =dataSource.getConnection();       
          //2、创建状态
          Statement state =con.createStatement();          
          //3、查询数据库并返回结果
          ResultSet result =state.executeQuery("select * from users");          
          //4、输出查询结果
          while(result.next()){
                 System.out.println(result.getString("email"));
          }           
          //5、断开数据库连接
          result.close();
          state.close();
          //6、归还数据库连接给连接池
          dataSource.releaseConnection(conn);
    }

  

影响因素:

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。数据库连接池的最小连接数和最大连接数的设置要考虑到下列几个因素:

1. 最小连接数
是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费。
2. 最大连接数
是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求将被加入到等待队列中,这会影响之后的数据库操作。
3. 最小连接数与最大连接数差距
最小连接数与最大连接数相差太大,那么最先的连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放。

原理:

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

作用:它大大提供了数据库连接的利用率,减小了内存吞吐的开销。

Java 连接池:

在 Java 中开源的数据库连接池有以下几种 :
1、C3P0:是一个开放源代码的 JDBC 连接池,它在 lib 目录中与 Hibernate一起发布,包括了实现 jdbc3 和 jdbc2 扩展规范说明的 Connection 和 Statement 池的 DataSources 对象。
2、Proxool:是一个 Java SQL Driver 驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的 JDBC 驱动程序增加连接池功能。
3、Jakarta DBCP:DBCP 是一个依赖 Jakartacommons-pool对象池机制的数据库连接池。DBCP 可以直接的在应用程序中使用。
4、DDConnectionBroker:是一个简单、轻量级的数据库连接池。
5、DBPool:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池。
6、XAPool:是一个 XA 数据库连接池。它实现了 javax.sql.XADataSource 并提供了连接池工具。
7、Primrose:是一个 Java 开发的数据库连接池。当前支持的容器包括 Tomcat4&5、Resin3 与 JBoss3。它同样也有一个独立的版本,可以在应用程序中使用而不必运行在容器中。Primrose 通过一个 WEB 接口来控制 SQL 处理的追踪、配置,以及动态池管理。在重负荷的情况下可进行连接请求队列处理。
8、SmartPool:是一个连接池组件,它模仿应用服务器对象池的特性。SmartPool 能够解决一些临界问题如连接泄漏(connection leaks)、连接阻塞、打开的 JDBC 对象(如 Statements、PreparedStatements)等。SmartPool 的特性包括:
  • 支持多个 pool
  • 自动关闭相关联的 JDBC 对象
  • 在所设定 time-outs 之后察觉连接泄漏
  • 追踪连接使用情况
  • 强制启用最近最少用到的连接
  • 把 SmartPool“包装”成现存的一个 pool
9、MiniConnectionPoolManager:是一个轻量级JDBC数据库连接池。它只需要 Java1.5(或更高)并且没有依赖第三方包。
10、BoneCP:是一个快速、开源的数据库连接池。帮用户管理数据连接,让应用程序能更快速地访问数据库。比 C3P0/DBCP 连接池速度快 25 倍。
11、Druid:Druid 不仅是一个数据库连接池,还包含一个 ProxyDriver、一系列内置的 JDBC 组件库、一个 SQL Parser。
支持所有 JDBC 兼容的数据库,包括 Oracle、MySql、Derby、Postgresql、SQL Server、H2 等。
Druid针对 Oracle 和 MySql 做了特别优化,比如:
  • Oracle 的 PS Cache 内存占用优化
  • MySql 的 ping 检测优化
Druid 提供了 MySql、Oracle、Postgresql、SQL-92 的 SQL 的完整支持,这是一个手写的高性能 SQL Parser,支持 Visitor 模式,使得分析 SQL 的抽象语法树很方便。
简单 SQL 语句用时 10 微秒以内,复杂 SQL 用时 30 微秒。
通过 Druid 提供的 SQL Parser 可以在 JDBC 层拦截 SQL 做相应处理,比如说分库分表、审计等。Druid 防御 SQL 注入攻击的 WallFilter,就是通过 Druid 的 SQL Parser 分析语义实现的