文章目录

  • 基本的JDBC操作方式
  • 基本的JDBC操作在高并发的情况下带来的问题
  • 第三方应该具备条件
  • 连接池的初步设计
  • C3P0从数据源获取到连接的过程
  • 如何从C3P0获取连接池状态信息

基本的JDBC操作方式

为了屏蔽掉连接各种数据库的底层细节,jdk提出了JDBC规范。数据库厂商则需要实现JDBC规范中的某些接口。这些实现则统称为XXX数据库驱动,比如我们常见的Mysql驱动com.mysql.jdbc.Driver。
使用JDBC调用数据库分为如下几步:

  1. 加载相关数据库驱动
  2. 获取与数据库的连接
  3. 执行SQL语句
  4. 关闭数据库连接

其对应的代码如下

            Class.forName(driver);   //记载数据库驱动conn = DriverManager.getConnection(url,username,password);    //获取与数据库的连接Long t1 = System.currentTimeMillis();System.out.println("获取连接用时:"+(t1-Time)+"毫秒");ps = conn.prepareStatement(sql);    rs = ps.executeQuery();    //  执行SQL语句Long t2 = System.currentTimeMillis();System.out.println("执行SQL语句用时:"+(t2-t1)+"毫秒");

其执行结果如下:

我们发现在整个过程中,获取与数据库的连接>>执行SQL语句。

这是由于JDBC连接是建立在TCP协议上的。每次获取到一条连接,其实是一条TCP连接。而TCP建立需要通过三次握手协议等一系列的操作来完成。因此,获取数据库连接的时间远大于SQL语句执行的时间。

基本的JDBC操作在高并发的情况下带来的问题

基本的JDBC操作在一些简单的情况下没有什么问题,比如课设。但是在并发量稍微增大后,就会变成如下的情况:

在并发量稍微提高后,如标准版登录过程会有十几个对API的请求。在这样的情况下,如果使用基本的JDBC操作,在当上一次请求还在获取JDBC的连接时,下一个请求就会到来。这样的话对CPU,内存等硬件资源消耗极大,从而导致性能下降。

总而言之,基本的JDBC操作在高并发的情况下带来下面问题:

  • 每次操作数据库都需要加载驱动、获取连接等等,代码十分繁琐。(编码方式上)
  • 建立连接的时间要远大于执行sql语句的时间。程序大量的时间都浪费在建立连接上了。
  • 每个请求都是交由一个线程来执行。在高并发的情况下,会伴随着大量的线程等待获取数据库连接请求。

这些问题的根源在与每次对数据库进行操作都需要重新建立一条新的连接,使用完成后再关闭连接。整个过程消耗了大量的资源。

第三方应该具备条件

针对上面的问题,这时自然而然会有一个想法:能不能每次执行完对数据库的操作,不关闭这些连接,而是把它交给一个第三方。等到下次想操作数据库时,再从这个第三方获取这些连接,这样就避免了每次需要建立连接和关闭连接的时间开销。

继续深入思考,这个第三方工具会有如下基本功能:

  1. 这个第三方应该能管理程序对数据库的连接。每次程序需要执行数据库操作的时候,都从这个第三方取出连接。执行完成后,就将连接还给这个第三方。
  2. 基于有穷性原则,这个第三方管理的连接数应该有一个上限。
  3. 为了应对突然而来的大量请求,这个第三方应该在应用程序初始化和空闲的时候管理一些空闲的连接。

这个第三方就是我们常说的数据库连接池。这里设计模式叫资源池。

池化技术能够减少资源对象的创建次数,提高程序的性能,特别是在高并发下这种提高更加明显。
使用池化技术缓存的资源对象有如下共同特点:
1,对象创建时间长;
2,对象创建需要大量资源;
3,对象创建后可被重复使用。

连接池的初步设计

对于一个连接池,最基本会有一个资源池,里面存放着与数据库的连接。基于上面所讲,这个连接应该是对java.sql.Connection的封装,里面应该会有一个标志位来代表这个Connection是否被占用。然后会有一个数据源管理类来管理这个连接池。同时应该有一个配置文件来初始化该连接池。

C3P0从数据源获取到连接的过程

从我们最熟悉的代码开始com.mchange.v2.c3p0.ComboPooledDataSource是C3P0提供给我们的数据源实现类。
这个可以从我们配置的Bean中可以看出。

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="${driverClass}"></property><property name="jdbcUrl" value="${jdbcUrl}"></property><property name="user" value="${en_username}"></property><property name="password" value="${en_password}"></property><property name="maxPoolSize" value="${maxPoolSize}"></property><property name="minPoolSize" value="${minPoolSize}"></property><property name="initialPoolSize" value="${initialPoolSize}"></property><property name="maxIdleTime" value="${maxIdleTime}"></property><property name="acquireIncrement" value="${acquireIncrement}"></property><property name="maxStatements" value="${maxStatements}0"></property><property name="idleConnectionTestPeriod" value="${idleConnectionTestPeriod}"></property><!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 --><property name="acquireRetryAttempts" value="30" /><property name="testConnectionOnCheckin" value="false"/><property name="testConnectionOnCheckout" value="true"/><property name="automaticTestTable" value="C3POTestTable"/><property name="preferredTestQuery" value="SELECT 1"/></bean>

再从程序如何从数据源中获取连接进入
下面代码是AbstractPoolBackedDataSource类中的getConnection方法

public Connection getConnection() throws SQLException
{//获取1.C3P0PooledConnectionPoolManager//获取2.C3P0PooledConnectionPool// 获取3.PooledConnectionPooledConnection pc = getPoolManager().getPool().checkoutPooledConnection();//从PooledConnection中获取我们的要用的Connectionreturn pc.getConnection();
}

下面的代码是C3P0PooledConnectionPool中的checkoutPooledConnection()方法代码细节

    public PooledConnection checkoutPooledConnection() throws SQLException{ //从rp中获取PooledConnectiontry { return (PooledConnection) rp.checkoutResource( checkoutTimeout ); }catch (TimeoutException e){ throw SqlUtils.toSQLException("An attempt by a client to checkout a Connection has timed out.", e); }catch (CannotAcquireResourceException e){ throw SqlUtils.toSQLException("Connections could not be acquired from the underlying database!", "08001", e); }catch (Exception e){ throw SqlUtils.toSQLException(e); }}

获取连接资源

    public Object checkoutResource( long timeout )throws TimeoutException, ResourcePoolException, InterruptedException{//获取资源Object resc = prelimCheckoutResource( timeout );if (resc == null)return checkoutResource( timeout );elsereturn resc;}
    private synchronized Object prelimCheckoutResource( long timeout )throws TimeoutException, ResourcePoolException, InterruptedException{try{ensureNotBroken();int available = unused.size();if (available == 0){int msz = managed.size();if (msz < max){// to cover all the load, we need the current size, plus those waiting already for acquisition, // plus the current client int desired_target = msz + acquireWaiters.size() + 1;if (logger.isLoggable(MLevel.FINER))logger.log(MLevel.FINER, "acquire test -- pool size: " + msz + "; target_pool_size: " + target_pool_size + "; desired target? " + desired_target);if (desired_target >= target_pool_size){//make sure we don't grab less than inc Connections at a time, if we can help it.desired_target = Math.max(desired_target, target_pool_size + inc);//make sure our target is within its boundstarget_pool_size = Math.max( Math.min( max, desired_target ), min );_recheckResizePool();}}else{if (logger.isLoggable(MLevel.FINER))logger.log(MLevel.FINER, "acquire test -- pool is already maxed out. [managed: " + msz + "; max: " + max + "]");}awaitAvailable(timeout); //throws timeout exception}Object  resc = unused.get(0);}

最终定位到三个变量managed,unused,excluded。

    /*  keys are all valid, managed resources, value is a PunchCard */ HashMap  managed = new HashMap();/* all valid, managed resources currently available for checkout */LinkedList unused = new LinkedList();/* resources which have been invalidated somehow, but which are *//* still checked out and in use.                                */HashSet  excluded = new HashSet();

在BasicResourcePool下面,管理着三个变量managed、unused和excluded,他们别代表着管理着的连接、未使用的连接,和使用中但未被管理的连接。

从数据源ComboPooledDataSource拿到连接池C3P0PooledConnectionPool的过程为
对于连接池C3P0PooledConnectionPool与资源池BasicResourcePool的关系为

如何从C3P0获取连接池状态信息

官方文档提供的查询池状态信息

基于上面对C3P0的了解。查阅AbstractPoolBackedDataSource。发现


对于getNumConnections()来说,其实现代码如下

C3P0PooledConnectionPoolpublic int getNumBusyConnections() throws SQLException{ try {synchronized ( rp )//{ return (rp.getAwaitingCheckinCount() - rp.getExcludedCount()); }}catch ( Exception e ){ //e.printStackTrace();logger.log( MLevel.WARNING, null, e );throw SqlUtils.toSQLException( e );}}
BasicResourcePoolpublic synchronized int getAwaitingCheckinCount(){ return managed.size() - unused.size() + excluded.size(); }
BasicResourcePoolpublic synchronized int getExcludedCount(){ return excluded.size(); }

确认方法如下

方法名 作用
getNumConnections() 池中管理的链接数
getNumBusyConnections() 池中已经在使用的链接数
getNumUnclosedOrphanedConnections() 已经在使用,但是未被连接池管理的连接

未完待续。。。

从JDBC到数据库连接池相关推荐

  1. Java -- JDBC 学习--数据库连接池

    JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模式开 ...

  2. JavaWeb:JDBC之数据库连接池

    JDBC系列阅读 JavaWeb:用JDBC操作数据库 JavaWeb:JDBC之事务 JavaWeb:JDBC之数据库连接池 使用JDBC实现水果超市管理系统 1. 池参数(所有池参数都有默认值) ...

  3. Java JDBC和数据库连接池 韩顺平老师自学笔记

    JDBC和数据库连接池 JDBC 概述 基本介绍 原理示意图 代码示例 JdbcInterface 模拟Java公司提供给其它数据库厂商的接口,供给调用 TestJdbc 模拟一个类来实现数据库的调用 ...

  4. 【JDBC】数据库连接池技术

    文章目录 一.数据库连接池技术 二.多种开源的数据库连接池 一.数据库连接池技术 1.数据库连接池的基本思想︰ 就是为数据库连接建立一个"缓冲池".预先在缓冲池中放入一定数量的连接 ...

  5. JDBC以及数据库连接池的使用

    文章目录 JDBC 步骤 数据库连接池 1.概念 2.接口规范方法 3.第三方数据库连接池技术 C3p0 Druid:由阿里提供 Druid工具类 JDBC 概念 ​ JDBC是sun公司提供的一套用 ...

  6. 基于JDBC的数据库连接池技术研究与应用

    引言 近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机 应用程序已从传统的桌面应用转到Web应用.基于B/S(Browser/Server)架构的3层开 ...

  7. 基于JDBC的数据库连接池高效管理策略

    2019独角兽企业重金招聘Python工程师标准>>> 介绍 在使用Java语言进行和数据库有关的的应用开发中,一般都使用JDBC来进行和数据库的交互,其中有一个关键的概念就是Con ...

  8. JDBC【数据库连接池、DbUtils框架、分页】

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

  9. kylin调优,项目中错误总结,知识点总结,kylin jdbc driver + 数据库连接池druid + Myba

    首先给大家分享一个巨牛巨牛的人工智能教程,是我无意中发现的.教程不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈-我正在学习中,觉得太牛了,所以分享给大家!点这里可以跳转 ...

最新文章

  1. 阿里P7架构师告诉你Java架构师必须知道的 6 大设计原则
  2. OpenCV2.4.9 显示鼠标框选区域,其他部分全为零,鼠标创建ROI区域
  3. SupeSite后台添加新闻增加【预览】功能
  4. 【Python】写视频的2种常用方法:write_videofile和videoWrite
  5. c# 获取当前活动窗口句柄,获取窗口大小及位置
  6. 初识C++之指针与引用
  7. python封装api给vue_Vue axios api统一管理的封装
  8. .net core json 为null输出_SpringBoot实战(九):标准化json返回值
  9. python生成器函数的使用(模拟cycle函数)
  10. 计算机软件版本号是什么意思,带你深入了解解密Windows系统版本和版本号
  11. 基于Python的指数基金量化投资——指数基金估值榜
  12. tilera netlib应用层协议栈-IP层实现方式
  13. Linux高级存储管理【2】(lvm快照、删除,vdo)
  14. HDOJ 3537 Daizhenyang's Coin (翻硬币游戏)
  15. 05 - 钓鱼网站的攻击与防御
  16. MOS管符号箭头指向问题
  17. Intellij IDEA - Did you kown...?
  18. 华为手机不小心点了始终_华为手机有一个设置,用过一次就再也离不开了,你打开了吗?...
  19. 亚马逊主图视频和买家秀视频有必要做吗?
  20. 解决margin塌陷问题

热门文章

  1. Potplayer使用必看
  2. 用c语言编程矩阵乘法,c语言矩阵相乘
  3. 什么是matlab中的fints函数,Matlab基本函数
  4. 工程制图 (点,线,面)
  5. 十六、去年写的pandas使用方法梳理,2020年5月13日整理
  6. 十三、深入Python字典和集合
  7. keras从入门到放弃(十七)使用预训练网络VGG迁移学习
  8. php 支付宝订单查询_5. PHP接入支付宝单笔订单查询接口
  9. C/C++Linux服务器开发/高级架构师
  10. 招募 | 贪心科技招募CV、语音分析、联邦学习课程讲师(在线+兼职)