概述

数据库连接池是负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。那么其中的运行机制又是怎样的呢?今天主要介绍一下数据库连接池原理和常用的连接池。

为什么要使用连接池

数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。 一个数据库连接对象均对应一个物理数据库连接,每次操作都打开一个物理连接,使用完都关闭连接,这样造成系统的性能低下。



数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并讲这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库连接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待。并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数。

  连接池技术尽可能多地重用了消耗内存地资源,大大节省了内存,提高了服务器地服务效率,能够支持更多的客户服务。通过使用连接池,将大大提高程序运行效率,同时,我们可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

不使用连接池流程

下面以访问MySQL为例,执行一个SQL命令,如果不使用连接池,需要经过哪些流程。



不使用数据库连接池的步骤:

  1. TCP建立连接的三次握手
  2. MySQL认证的三次握手
  3. 真正的SQL执行
  4. MySQL的关闭
  5. TCP的四次握手关闭

可以看到,为了执行一条SQL,却多了非常多网络交互

优点:

实现简单

缺点:

  • 网络IO较多
  • 数据库的负载较高
  • 响应时间较长及QPS较低
  • 应用频繁的创建连接和关闭连接,导致临时对象较多,GC频繁
  • 在关闭连接后,会出现大量TIME_WAIT 的TCP状态(在2个MSL之后关闭)

使用连接池流程



第一次访问的时候,需要建立连接。 但是之后的访问,均会复用之前创建的连接,直接执行SQL语句。

优点:

  1. 较少了网络开销
  2. 系统的性能会有一个实质的提升
  3. 没了麻烦的TIME_WAIT状态

数据库连接池的工作原理

连接池的工作原理主要由三部分组成,分别为

  1. 连接池的建立
  2. 连接池中连接的使用管理
  3. 连接池的关闭

第一、连接池的建立。

  一般在系统初始化时,连接池会根据系统配置建立,并在池中创建了几个连接对象,以便使用时能从连接池中获取。连接池中的连接不能随意创建和关闭,这样避免了连接随意建立和关闭造成的系统开销。

Java中提供了很多容器类可以方便的构建连接池,例如Vector、Stack等。

第二、连接池的管理。

  连接池管理策略是连接池机制的核心,连接池内连接的分配和释放对系统的性能有很大的影响。其管理策略是:

当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在空闲连接,则将连接分配给客户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果超出最大等待时间,则抛出异常给客户。

当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过就从连接池中删除该连接,否则保留为其他客户服务。

该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源开销。

第三、连接池的关闭。

当应用程序退出时,关闭连接池中所有的连接,释放连接池相关的资源,该过程正好与创建相反。

连接池需要注意的点

1、并发问题

为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。

这个问题相对比较好解决,因为各个语言自身提供了对并发管理的支持像java,c#等等,使用synchronized(java)lock(C#)关键字即可确保线程是同步的。

2、事务处理

我们知道,事务具有原子性,此时要求对数据库的操作符合“ALL-OR-NOTHING”原则,即对于一组SQL语句要么全做,要么全不做。

我们知道当2个线程共用一个连接Connection对象,而且各自都有自己的事务要处理时候,对于连接池是一个很头疼的问题,因为即使Connection类提供了相应的事务支持,可是我们仍然不能确定那个数据库操作是对应那个事务的,这是由于我们有2个线程都在进行事务操作而引起的。

为此我们可以使用每一个事务独占一个连接来实现,虽然这种方法有点浪费连接池资源但是可以大大降低事务管理的复杂性。

3、连接池的分配与释放

连接池的分配与释放,对系统的性能有很大的影响。合理的分配与释放,可以提高连接的复用度,从而降低建立新连接的开销,同时还可以加快用户的访问速度。

对于连接的管理可使用一个List。即把已经创建的连接都放入List中去统一管理。每当用户请求一个连接时,系统检查这个List中有没有可以分配的连接。如果有就把那个最合适的连接分配给他,如果没有就抛出一个异常给用户。

4、连接池的配置与维护

连接池中到底应该放置多少连接,才能使系统的性能最佳?

系统可采取设置最小连接数(minConnection)和最大连接数(maxConnection)等参数来控制连接池中的连接。比方说,最小连接数是系统启动时连接池所创建的连接数。如果创建过多,则系统启动就慢,但创建后系统的响应速度会很快;如果创建过少,则系统启动的很快,响应起来却慢。这样,可以在开发时,设置较小的最小连接数,开发起来会快,而在系统实际使用时设置较大的,因为这样对访问客户来说速度会快些。最大连接数是连接池中允许连接的最大数目,具体设置多少,要看系统的访问量,可通过软件需求上得到。

如何确保连接池中的最小连接数呢?有动态和静态两种策略。动态即每隔一定时间就对连接池进行检测,如果发现连接数量小于最小连接数,则补充相应数量的新连接,以保证连接池的正常运转。静态是发现空闲连接不够时再去检查。

数据库连接池的使用

c3p0

package cn.soboys.kenx.datasource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties; /*
* jdbc 通过数据连接池 c3p0 获取数据库连接
*/
public class C3p0DataSource {
//通过xml配置文件
public Connection getConnectionByC3p0PoolXml() {
//创建数据库连接池时,不传参数默认指default-config中参数,传入参数,则根据<name-config name="myc3p0">中的名字去找
try {
ComboPooledDataSource cpds = new ComboPooledDataSource();
//或者不使用默认的
ComboPooledDataSource cpds2 = new ComboPooledDataSource("myc3p0"); Connection conn = cpds.getConnection();
System.out.println(cpds.getProperties());//这里可以输出mysql的用户名和密码;
System.out.println(cpds.hashCode());//这里是输出连接的地址。 System.out.println("数据库连接成功");
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
} } //通过代码配置
public Connection getConnectionByC3p0PoolCode() {
//创建数据库连接池时,不传参数默认指default-config中参数,传入参数,则根据<name-config name="myc3p0">中的名字去找
try {
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.cj.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/kenx_test?characterEncoding=utf-8");
cpds.setUser("root");
cpds.setPassword("root");
cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(10);
cpds.setCheckoutTimeout(3000);
Connection conn = cpds.getConnection();
System.out.println("数据库连接成功");
return conn;
} catch (Exception e) {
e.printStackTrace();
return null;
} } //通过属性文件配置,默认c3p0不支持属性文件自动装配 需要我们自己手动设置属性
public Connection getConnectionByC3p0PoolProperties() {
//创建数据库连接池时,不传参数默认指default-config中参数,传入参数,则根据<name-config name="myc3p0">中的名字去找
try {
ComboPooledDataSource cpds = new ComboPooledDataSource(); //加载配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/c3p0.properties")); String driver = properties.getProperty("driverClassName");
String url = properties.getProperty("jdbcUrl");
String username = properties.getProperty("username");
String password = properties.getProperty("password"); cpds.setDriverClass(driver);
cpds.setJdbcUrl(url);
cpds.setUser(username);
cpds.setPassword(password); cpds.setInitialPoolSize(5);
cpds.setMaxPoolSize(10);
cpds.setCheckoutTimeout(3000);
Connection conn = cpds.getConnection();
System.out.println("数据库连接成功");
return conn;
} catch (Exception e) {
e.printStackTrace();
return null;
} } }

dbcp

package cn.soboys.kenx.datasource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory; import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.util.Properties; /*
* jdbc 通过数据连接池 DBCP 获取数据库连接
*/
public class DbcpDataSource { //通过代码配置
public Connection getConnectionByDBCPPoolCode() { try {
BasicDataSource bds = new BasicDataSource();
bds.setUrl("jdbc:mysql://localhost:3306/kenx_test?characterEncoding=utf-8");
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUsername("root");
bds.setPassword("root");
bds.setInitialSize(5);
Connection conn = bds.getConnection();
System.out.println("数据库连接成功");
return conn;
} catch (Exception e) {
e.printStackTrace();
return null;
} } //通过属性文件配置,数据源工厂不需要手动配置单个属性
public Connection getConnectionByDBCPPoolProperties() {
Properties prop = new Properties();
try {
prop.load(new FileInputStream("src/main/resources/dbcp.properties"));
BasicDataSource bds = BasicDataSourceFactory.createDataSource(prop);
System.out.println("数据库连接成功");
return bds.getConnection();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} }

durid

package cn.soboys.kenx.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.util.Properties; /*
* jdbc 通过数据连接池 Druid 获取数据库连接
*/
public class DruidDatasource { //通过属性文件配置,数据源工厂不需要手动配置单个属性
public Connection getConnectionByDuridPoolProperties() {
Properties prop = new Properties();
try {
prop.load(new FileInputStream("src/main/resources/durid.properties"));
// 获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
System.out.println("数据库连接成功");
return dataSource.getConnection();
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
} //通过代码配置
public Connection getConnectionByDBCPPoolCode() { try {
DruidDataSource bds=new DruidDataSource();
bds.setUrl("jdbc:mysql://localhost:3306/kenx_test?characterEncoding=utf-8");
bds.setDriverClassName("com.mysql.cj.jdbc.Driver");
bds.setUsername("root");
bds.setPassword("root");
bds.setInitialSize(5);
Connection conn = bds.getConnection();
System.out.println("数据库连接成功");
return conn;
} catch (Exception e) {
e.printStackTrace();
return null;
} }
}

完整项目案例

点击这里 github

JDBC | 第七章: JDBC数据库连接池使用的更多相关文章

  1. [原创]java WEB学习笔记80:Hibernate学习之路--- hibernate配置文件:JDBC 连接属性,C3P0 数据库连接池属性等

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. JDBC第四篇--【数据库连接池、DbUtils框架、分页】

    1.数据库连接池 什么是数据库连接池 简单来说:数据库连接池就是提供连接的. 为什么我们要使用数据库连接池 数据库的连接的建立和关闭是非常消耗资源的 频繁地打开.关闭连接造成系统性能低下 编写连接池 ...

  3. JDBC编程学习笔记之数据库连接池的实现

    在JDBC编程的时候,获取到一个数据库连接资源是很宝贵的,倘若数据库访问量超大,而数据库连接资源又没能得到及时的释放,就会导致系统的崩溃甚至宕机.造成的损失将会是巨大的.再看有了数据库连接池的JDBC ...

  4. Spring JDBC模版以及三种数据库连接池的使用

    jar包版本有点乱,直接忽略版本号,将就一下. 这里引了aop包是因为在spring3版本之后用模版对数据库库操作时会出现问题,但是不会报错,也没有提示. 所以这里直接引入,以及之后会用到的DBCP与 ...

  5. JDBC01 利用JDBC连接数据库【不使用数据库连接池】

    目录: 1 什么是JDBC 2 JDBC主要接口 3 JDBC编程步骤[学渣版本] 5 JDBC编程步骤[学神版本] 6 JDBC编程步骤[学霸版本] 1 什么是JDBC JDBC是JAVA提供的一套 ...

  6. JAVA / MySql 编程——第七章 JDBC

    1.JDBC:JDBA是Java数据库连接(Java DataBase Connectivity)技术的简称,提供连接各种常用数据库的能力:         ●Java是通过JDBC技术实现对各种数据 ...

  7. JDBC (Java DataBase Connectivity)数据库连接池原理解析与实现

    一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...

  8. Java Web(九) JDBC及数据库连接池及DBCP,c3p0,dbutils的使用

    DBCP.C3P0.DBUtils的jar包和配置文件(百度云盘):点我下载 JDBC JDBC(Java 数据库连接,Java Database Connectify)是标准的Java访问数据库的A ...

  9. JAVA之JDBC数据库连接池总结篇

    JDBC数据库连接池 一.JDBC数据库连接池的必要性 二.数据库连接池技术 三.多种开源的数据库连接池 3.1 C3P0数据库连接池 3.2 DBCP数据库连接池 3.3 Druid(德鲁伊)数据库 ...

随机推荐

  1. Pr剪辑

    目录 Pr剪辑教程 入门基础 创建序列类别 处理非正常序列 导出文件 导出设置 导入各类别素材 简单使用: 剪辑素材常用方法 剃刀工具 选择工具 波纹编辑工具 打入点和出点 剪辑速度 整个素材视频速度 ...

  2. IntelliJ IDEA 修改内存大小,使得idea运行更流畅。(转发)

    原文地址:https://blog.csdn.net/qq_27093465/article/details/81947933 idea有个配置文件,可以设置内存大小的,就跟咱的jvm的内存里面的堆大 ...

  3. CF1349F 【Slime and Sequences】part1

    由于本文过长,\(\LaTeX\) 炸了,分两篇,part2 题目描述 定义一个正整数序列为好序列,当且仅当如果某个数 \(k\) 出现过,那么一定有 \(k-1\) 在最后一个 \(k\) 的前面出 ...

  4. Java web 小测验

    题目要求: 1登录账号:要求由6到12位字母.数字.下划线组成,只有字母可以开头:(1分) 2登录密码:要求显示“• ”或“*”表示输入位数,密码要求八位以上字母.数字组成.(1分) 3性别:要求用单 ...

  5. 打破你的认知!Java空指针居然还能这样玩,90%人不知道…

    相信在座的各位都遇到过空指针异常,不甚其烦,本文不是教你避免空指针,而是一些对空指针其他方面的理解. 本文可能有点另类,也可能会打破你对空指针的认知. 1.null.method() 空指针? 我们知 ...

  6. 【工具】之002-Mac下常用工具

    写在前面 我很懒,,,不想敲一个命令一个命令敲... "偷懒是有前提的,不是之前,就是之后." 常用命令 测试端口是否畅通 nc -z 10.254.3.86 30003 查看端口 ...

  7. 使用docker快速搭建hive环境

    记录一下使用docker快速搭建部署hive环境 目录 写在前面 步骤 安装docker 安装docker 安装docker-compose 配置docker国内镜像源(可选) 安装git & ...

  8. C#LeetCode刷题之#530-二叉搜索树的最小绝对差(Minimum Absolute Difference in BST)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4123 访问. 给定一个所有节点为非负值的二叉搜索树,求树中任意两 ...

  9. C#LeetCode刷题之#657-机器人能否返回原点(Robot Return to Origin)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3959 访问. 在二维平面上,有一个机器人从原点 (0, 0) 开 ...

  10. C#LeetCode刷题之#581-最短无序连续子数组( Shortest Unsorted Continuous Subarray)

    问题 给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序. 你找到的子数组应是最短的,请输出它的长度. 输入: [2, 6, 4, 8, 10, ...