查看: 1549|回复: 0

[Java学习] 2017年——7年百度大神解析数据库连接池

发表于 2018-1-17 20:41:21
1.定义
百度百科中是这样说的,数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
对于初学者来说似乎看起来有点蒙,太官方了。我个人的理解是用来更好地管理数据库连接的,你可能会说,什么是数据库连接?ok,那接下来我们就从数据库连接开始聊起,最后再回归到数据库连接池。
2.数据库连接(对象)
先给大家说明一点,本文中我是以Java为例进行演示的,其他开发语言原理也类似。废话不多说,咱们开始。在开发中,程序中产生的数据往往需要存储到一个介质中,这个介质可以是文件,可以是数据库等等,或者要从某个地方把数据读取过来供程序使用,这里也就是我们所说的增删改查(crud)操作,说白了,也就是程序需要和存储介质进行一个交互,交流。
其中数据库是使用比较多的存储介质,那么问题就是程序与数据库之间如何进行数据的传输?这里就需要借助一个媒介,它俩之间沟通的一个桥梁,于是就引出了JDBC的概念,全称是Java DataBase Connectivity,即Java数据库连接。顾名思义,也就是在java程序中需要连接数据库的一种技术,一种媒介。
接下来问题就是如何使用这种JDBC技术呢?我们知道,Java是一门面向对象的开发语言,凡是都得想到对象的操作,也就是如果想使用JDBC去操作数据库,只要了解JDBC中的相关类(对象)即可,了解这些对象的含义以及对象中属性方法的使用就能熟练地对数据库进行操作。
还有一个值得关心的问题是,两者(即程序和数据库)沟通,目前确定的一方是Java,数据库我们知道,有很多种,比MySQL,SQLServer,Oracle等。这时候你可能会想,这些数据库可能属于不同的产商,那么Java对它们的操作技术都是一样的吗?实际上,对于不同的数据库,在具体实现方面可能会有一些微小的差别,不过没关系,在Java中,定义的是接口,然后交给不同的数据库厂商具体实现。说白了就是在jdk的java.sql包中,定义了很多接口,这些接口中的方法是操作每种数据库必备的一些方法,在具体连接某种数据库,比如mysql数据库时,需要导入一个MySQL数据库的jar包,该jar包中的类实现了这些接口,这样就可以完美地解决JDBC连接不同数据库的差异性问题。
好了,上面扯了这么多,还没聊到正题,那什么是数据库连接呢?因为数据库连接池管理的是数据库连接,更准确的说是一个个数据库连接对象。这里我们以java语言和MySQL数据库为例进行说明,怎么让两者发生关系?我们知道需要使用JDBC技术,那怎么使用JDBC技术,我们知道需要使用到一些类,那到底使用哪些类?下面就来看下具体的步骤:
(1)导入MySQL数据库jar包和加载MySQL数据库驱动。至于为什么需要导入MySQL数据库jar包,上面已经进行了描述,在jdk中定义了一系列接口,具体不同的数据库要进行各自的实现,所以需要导入各自的数据库jar包。加载数据库驱动可以理解为,要想使用MySQL数据库jar包中的一些类,必须先把驱动类加载到虚拟机中。
(2)具体需要连接哪个数据库,需要使用到Connection这个类。比如在MySQL数据库中由很多的dbtest,dbabc等数据库,通过该对象就可以明确到底要连接到哪个数据库。代码体现就是Connection conn = (Connection) DriverManager.getConnection(url, username, password),这样可以拿到一个具体的连接对象,也就是通过该对象可以连接到某个具体的数据库。
(3)再接下来就是需要进行查询,增加,修改或者删除等操作了,需要准备一个sql语句和Statement对象,这个Statement对象就是具体干活的,也就是Connection对象负责连接到具体的某个数据库,其他事情它就不管了,而其他的操作就需要其他的对象来进行。因为这篇文章主要是介绍数据库连接池,为了不跑题,对于JDBC操作数据库的细节就不多赘述咯。
要朝这java程序员发展或者真心有兴趣的。可以找我要一些java的学习视频java学习交流群:450936584,这个是免费的,希望同学找我要的时候不要有理所应当的态度,毕竟都是我的心血,希望你是真的有一颗想要学好java的心,我也会尽所能的去帮助你成为一名优秀的程序员。
3.问题描述
ok,通过上面的过程描述就可以知道什么是数据库连接(对象),也就是Connection。该对象的作用是连接到某个具体的数据库,当用户需要对数据表进行操作时,就需要先连接到对应的数据库,拿到一个Connection对象然后进行其他操作,试想一下,要是一个应用一天有1万个用户进行访问,这样的连接操作就要进行一万次。而数据库连接是非常消耗资源的,也是非常宝贵的,这样会大大减低性能,什么是性能?可以理解为每次拿这个连接的时候,数据库的压力都很大,消耗的时间都很长等等。
4.问题解决
既然每次访问都需要拿到一个Connection对象,而每次拿Connection对象又非常消耗资源,那该如何解决呢?所以数据库连接池应运而生,所谓“池”就是相当于一个容器,这个容器就是保存数据库连接(对象)的。具体做法就是,在应用程序刚启动时,先和数据库拿到一批Connection对象,比如20个,保存在这个容器(池)中,于是这个容器(池)就称为“数据库连接池”,也就是保存数据库连接对象的池。
这样做有什么好处呢?当用户访问的时候,要操作数据库,肯定先要拿到一个Connection对象,而此时就不是和数据库去拿了,是和这个“数据库连接池”去拿,在用完这个Connection对象关闭的时候,也不是将该连接还回到数据库中,而是还到“数据库连接池”中,说白了就是,需要连接和池要,用完了还连接还给池。这样数据库的压力就大大减轻了,因为用户每次要连接不用和数据库要了,这些连接在应用程序刚启动时已经和数据库要了一批。
这时候你会想,万一20个连接不够用咋办?万一20个连接嫌多咋办?万一...所有的这些万一,无非是连接个数多了少了,对不对?多了的情况,好解决,大不了释放一些连接,因为放着也是浪费;少了的情况,大不了再和数据库申请一批。如果是申请了还不够用,比如30个连接了,但是31个用户同时并发访问,此时肯定有一个用户访问不了,咋办呢?那就等呗,等多久?要么就是当有用户还连接到“数据库连接池”了,要么就是看配置的情况,想让你等多久就等多久。实际上上面所说的这些问题都是可以通过配置数据库连接池进行控制的,也就是平时我们所看到的,init,max等这些配置细节,很简单,一看就懂,接下来我们具体使用“数据库连接池”的时候稍微解释就明白咯。
5.数据库连接池实现
整个流程都明白了,原理也清楚了,那么这个“池”到底怎么生产出来?怎么用?用的时候要注意些什么?等等一系列问题。别担心,我们一步步来,先来看这个“池”如何生产出来,这时候大家会想,一定又是万物皆对象的概念,肯定有这么一个“数据库连接池的对象”供我们使用。想法是没错的,实际上也是如此,要想生产出来一个数据库连接池,就要找类或者是接口,大家知道,接口实际上是完全的一个抽象类,也属于类。于是我们找了找,发现在javax.sql包中有一个DataSource的接口,单词的含义是“数据源”,这里和大家补充说明一点,“数据源”实际上等同于“数据库连接池”,看到这个单词,就把它当成“数据库连接池”即可。按照我们的想法,要想得到一个数据库连接池,只要写一个类实现该接口即可,不妨试试?ok,那就试试,细节一会再说。
于是我就写了这么一个类,名称为"MyDataSource",并实现了DataSource接口,那此时我就定义出了一个自己的“数据库连接池”,回想一下,数据库连接池的作用,一开始需要和数据库要一批连接对象过来,比如是20个。那这20个保存在哪边?可以保存到List集合中,这20个连接在一开始就要申请,于是可以将申请20个的动作放到静态代码块中,如下图所示。
其中有一个方法叫做“getConnection”,可以看出该方法就是获取数据库连接的,里面怎么写呢?先判断一下lists集合中是否还有空余的连接,如果有则返回,如果没有则抛出异常,代码如下图所示。
好了,在使用的时候,要获取到数据连接对象,就不是从数据库中获取了,而是从数据库连接池中获取,如下图所示
一切看起来那么完美,应用程序刚启动时获取一批数据库连接,程序中需要连接对象时和数据连接池要,似乎咱们忘了一个步骤,那就是用完数据库连接对象之后,还需要将数据库连接对象还到数据库连接池中,大家可能会想,这还不简单,close一下不就完事了。但是咱们注意,当我们调用Connection对象的close方法时,这个连接到底还给了“数据库连接池”还是“还给了数据库”,显然是还给了“数据库”,这样肯定不行。一开始数据库连接池和数据库申请了20个连接,然后每次用的时候从数据库连接池中取出连接,还的时候还给数据库,到最后,数据库连接池中的连接就会没了,违背了我们的设计思路。
现在问题在于当用完数据库连接对象之后,还要还给数据库连接池,而不是还给数据库,也就是当调用Connection对象的close方法时,需要将连接添加到数据库连接池中,也就是我们上面设计的lists集合中,这样才可以保持一个借还平衡。
归根到底,就是要对close方法进行改造,不能用原来jdbc中的close方法,那怎么改造呢?方式有很多,可以使用子类继承父类(Connection类)然后复写其中的close方法,但是这种做法比较麻烦,而且还要重写父类中的所有方法,不太合理。还可以使用包装设计模式和动态代理。不过对于初学者写起来也是一件比较费劲的事情。另外还有很多细节需要考虑,比如数据库连接池的初始化大小,最大连接数,最长等待时间等等,这时候大家会想,原来写一个自己的数据库连接池(数据源)也不容易啊,最好是能有人帮我们写好了,然后咱们直接用就好了,因为数据库连接池的这种思想的确是好,可以大大提升我们访问数据库的性能。
既然数据库连接池这么有必要,那肯定有人帮我们已经做好了这些事情,这里就要提到市面上很多数据库连接池框架,比如dbcp,c3p0等,名称看起来很奇怪,但是无妨,只是一个命名,读多了也就习惯了。这些数据库连接池的使用也非常简单,我们就以常用的c3p0为例,看看别人写好的数据库连接池怎么用。
6.数据库连接池c3p0的使用
(1)在Java中,要想使用一门其他的技术,就得先想到导入jar包
(2)要想使用一个数据库连接池,就需要新建一个该数据库连接池的对象,然后进行相关的配置。当然这些配置可以写到一个配置文件中,这样更加灵活,为了演示方便,我这里就直接硬编码到程序中。
(3)分析
上图所示的,第一步是新建一个数据库连接池(数据源)对象,然后设置一下jdbc中相关的属性,有驱动类,url,username,password等,这些都是连接具体某个数据库必备的信息,我们需要告诉数据库连接池。对于下面几个属性的配置,就是初始化数据连接池的大小,也就是有多少个数据库连接对象,还有最大,最小的的数据库连接对象数,最大的空余时间连接对象数等等,也就是上面我们所担心的各种万一,在数据库连接池中都可以进行配置。然后数据库连接池就会根据我们的设置,从而创建出对应数量的数据库连接对象。
(4)得到数据库连接池中的数据库连接对象
(5)之前所担心的数据库连接对象归还到哪里的问题
之前我们分析过,数据库连接对象用完之后,怎样让其归还到数据库连接池中,可以有很多的解决方式,其中比较常用的是包装设计模式和动态代理。不管用哪种,最终我们通过数据库连接池拿到的Connection对象肯定不是MySQL中的Connection对象,因为需要对其中的close方法进行改造,所以肯定不能用原来的对象。我们可以追踪一下c3p0的源码,发现其使用的是动态代理方式实现对Connection对象的增强。



回复

使用道具 举报