数据库连接池-连接的关闭内幕

简介: 们经常会遇到这样那样的连接未关闭的问题,连接没有及时关闭导致的直接后果就是内存泄漏直至down机。我们也都知道解决的方式,但是在解决了问题之后经常会思考为什么会这样呢?连接close()掉,然后在创建不是很浪费cpu等系统资源嘛?有没有更好的方法解决呢?大家也经常听到连接池、线程池之类的线程、池的概念,那么究竟这些概念与我们的连接有什么关系呢?        下面我就想就上面的问题谈谈我
们经常会遇到这样那样的连接未关闭的问题,连接没有及时关闭导致的直接后果就是内存泄漏直至 down 机。我们也都知道解决的方式,但是在解决了问题之后经常会思考为什么会这样呢?连接 close() 掉,然后在创建不是很浪费 cpu 等系统资源嘛?有没有更好的方法解决呢?大家也经常听到连接池、线程池之类的线程、池的概念,那么究竟这些概念与我们的连接有什么关系呢?

       下面我就想就上面的问题谈谈我的一点浅见,请大家批评指正。

       大家都知道java语言是一种语言级的多线程机制的面向对象语言。比如说,java的基类object,它就有一些诸如wati(),notify(),notifyall()等线程控制的方法。如果大家学习过操作系统的化,我想应该知道线程存在同步和异步的问题,解决的方法很多,其中就有“信号量机制”实现线程的同步,java的这种同步机制与操作系统大同小异。同步机制是一个比较复杂的问题,如果感兴趣可以找一本操作系统的书看看。

       下面简单介绍一些进程和线程的概念:

1.         进程:

       进程是资源分配和独立运行的基本单位。进程的定义很多,下面列举一些

Ø         进程是程序的一次执行;

Ø         进程是可以和别的计算机并发执行的计算;

Ø         进程可定义为一个数据结构及能在其上进行操作的一个程序

Ø         进程是一个程序及其数据在处理机上顺序执行时发生的活动;

Ø         进程时程序在一个数据集合上的运行过程,时系统进行资源分配和调度的一个独立单位。

2.         线程

由于进程是一个资源拥有者,因而在进程的创建、撤消和切换过程中,系统必须为之付出较大的时空开销。也正因为如此,在系统中所设置的进程数目不宜过多,进程切换的频率也不宜过高,但这也就限制了并发程度的进一步提高。因此便引出了线程的概念

把线程作为调度和分派的基本单位,而把进程作为资源拥有的基本单位,使传统进程的两个属性分开,线程便能轻装运行,从而显著提高系统的并发程度。

Ø         在同一个进程内可以有多个线程;

Ø         同一个进程内的线程切换不会引起进程切换;

Ø         一个进程的线程切换到另一个进程的线程时会引起进程切换

3.         JSP/SERVLET

而我们常用的jsp/ervlet这种j2ee的体系结构正是建立在java的多线程机制之上的。JSP/SERVLET容器会自动使用线程池等技术来支持系统的运行。因此,JSP/SERVLET的实质是一种线程技术,JSP会在运行时被编译成servlet来运行,如图所示:

点击查看原始大小 512 x 384

       当客户端向服务器发出一个请求时,servlet容器会分配一个线程专门处理这个请求,线程内容就是JSP/servlet应用程序。

       这部分内容与本主体无关,只是顺便说说。

4.         线程池

       首先介绍一下线程池:

       线程的创建和销毁,以及切换,执行都是要耗费系统资源的。当系统访问量比较大的时候,服务器内就会创建太多的线程,直至资源完全消耗,这对于应用系统的正常运行是有致命伤害的。

       为了能够在访问尖峰时限制活动线程的数量,同时减少线程频繁创建和销毁带来的系统开销,提高系统的大访问量的处理性能和速度,需要事先创建一定数量的线程供调用者循环反复使用,这就是“池”技术。

       线程的基本原理是基于队列queue这种数据结构的,通过不断查询队列queue是否有可以运行的线程。如果有,就立即运行线程,没有,则锁定等待,直到有新的线程加入被解锁。(这种锁定机制,就是所谓的“信号量机制”)。

       一种线程池必须解决如下的问题:死锁、资源不足、并发错误、线程泄漏和请求过载。下面我们具体举一个成熟的开源线程池的例子来说明线程池的原理:

    PooledExecutor pool=new PooledExecutor(new BoundedBuffer(20),100);

    pool.setMinimumPoolSize(10);//最小线程数为10

    poole.setKeepAliveTime(-1);//线程一直运行

    上面的语句设置了线程的最大数目为100,这样,就可以保护系统不会因为访问量增加导致线程数目的无限增加。使用该线程池如下:

       pool.execute(java.lang.Runnable 自己的线程);

       这一句实际上是将“自己的线程”加入一个队列中,而队列(先进先出FIFO)另一段正开启多个线程不断读取这个队列,一旦队列中有空闲的线程,线程管理器就将读取并分配线程来运行它。

      

    public void execute(Runnable command) throws InterruptedException {

       for (;;) { //一直循环

           synchronized (this) {

              if (!shutdown_) { //确保线程池没有关闭

                  int size = poolSize_; //当前线程池中线程的数目

                  if (size < minimumPoolSize_) { //如果当前线程数目少于线程池最小数目

                     addThread(command);

                     return;

                  }

                  //如果目前线程池中有超过或等于最小数目的线程

                  //分配一个存在的空闲线程来运行commandhandOff是队列

                  if (handOff_.offer(command, 0)) {

                     return;

                  }

                  //如果不能分配已有的线程来运行command,那么创建一个新线程

                  if (size < maximumPoolSize_) {

                     addThread(command);

                     return;

                  }

              }

           }

           //如果阻塞,则请求帮助

           if (getBlockedExecutionHandler().bolckedAction(command)) {

              return;

           }

       }

    }

       由上面的代码可见,PooledExecutor线程池的原理是,当执行execute加载一个应用系统的线程时,线程池内部首先检查当前线程数目是否达到设定的最小数目。如果没有达到,启动新线程运行;如果达到了,那么检查有无空闲线程可用;如果没有空闲的,则创建新线程,直到达到最大数目。

       使用线程池的好处是:首先是循环使用,一经创建后,空闲的线程可以被反复使用,提高了运行效率;其次有最大数目的限制,保证了系统的安全性。

5.         连接池

终于轮到连接池了,通过上面的介绍,我们对线程及线程池都有个一个大致的了解。

在正常情况下,直接使用JDBC调用数据库可以满足一个小型系统的要求,但是当系统规模比较大的情况下,就会出现数据库的访问量迅速提升而令服务器不堪重负的现象,因而为了解决这一性能问题,常常会使用数据库连接池作为一个缓存的方式解决。

连接池类似上面介绍的线程池。

每次数据库连接的建立都需要花费一定的时空费用,而使用连接池,可以事先建立连接。当应用程序需要开始使用时,就从连接池中获取一个连接使用,应用程序使用完毕,通过close()方法将连接归还连接池讲到这里,我门就不必在担心close()方法会不会影响性能了,完全可以放心大胆的使用。因为,它实际上并没有关闭连接,而是将连接归还连接池,供下次使用

当并发增加是,连接池会不断的自动创建新的连接满足调用,直到达到连接池的最大数目;当连接池连接减少甚至没有时,连接池自动关闭一些连接,保持最小数目。

因此连接池的使用节省了连接建立时间,消除了数据库频繁连接带来的开销和瓶颈

小提示:不知道大家有没有注意到配置websphere时有关于连接池最大最小数目的配置。呵呵,道理就在这。

那么,我们经常面对的连接未关闭的问题导致的系统速度很慢的问题就很容易说明了,就是因为线程池已经达到了最大数目,没有可用的了。所以,其他操作只有等待的份,等待那些应用用完了,被垃圾回收了,才能释放出可用的连接。 

目录
相关文章
|
2月前
|
SQL 开发框架 .NET
ASP.NET连接SQL数据库:详细步骤与最佳实践指南ali01n.xinmi1009fan.com
随着Web开发技术的不断进步,ASP.NET已成为一种非常流行的Web应用程序开发框架。在ASP.NET项目中,我们经常需要与数据库进行交互,特别是SQL数据库。本文将详细介绍如何在ASP.NET项目中连接SQL数据库,并提供最佳实践指南以确保开发过程的稳定性和效率。一、准备工作在开始之前,请确保您
282 3
|
1月前
|
关系型数据库 MySQL 数据库连接
python脚本:连接数据库,检查直播流是否可用
【10月更文挑战第13天】本脚本使用 `mysql-connector-python` 连接MySQL数据库,检查 `live_streams` 表中每个直播流URL的可用性。通过 `requests` 库发送HTTP请求,输出每个URL的检查结果。需安装 `mysql-connector-python` 和 `requests` 库,并配置数据库连接参数。
132 68
|
1月前
|
数据库 C# 开发者
ADO.NET连接到南大通用GBase 8s数据库
ADO.NET连接到南大通用GBase 8s数据库
|
28天前
|
数据库连接 Linux Shell
Linux下ODBC与 南大通用GBase 8s数据库的无缝连接配置指南
本文详细介绍在Linux系统下配置GBase 8s数据库ODBC的过程,涵盖环境变量设置、ODBC配置文件编辑及连接测试等步骤。首先配置数据库环境变量如GBASEDBTDIR、PATH等,接着修改odbcinst.ini和odbc.ini文件,指定驱动路径、数据库名称等信息,最后通过catalog.c工具或isql命令验证ODBC连接是否成功。
|
1月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
55 3
|
27天前
|
JSON JavaScript 关系型数据库
node.js连接GBase 8a 数据库 并进行查询代码示例
node.js连接GBase 8a 数据库 并进行查询代码示例
|
2月前
|
关系型数据库 MySQL 数据库连接
DBeaver如何连接一个数据库
【10月更文挑战第27天】DBeaver 是一款功能强大的通用数据库管理工具,支持多种主流数据库。本文介绍了使用 DBeaver 连接数据库的基本步骤,包括下载安装、创建新连接、选择数据库类型、配置连接参数、测试连接以及最终连接到数据库。详细的操作指南帮助用户轻松管理和操作数据库。
419 9
|
28天前
|
数据库连接 数据库 C#
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(上)
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(上)
|
28天前
|
数据库连接 数据库 C#
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(下)
本文接续前文,深入讲解了在Windows环境下使用C#和ADO.NET操作南大通用GBase 8s数据库的方法。通过Visual Studio 2022创建项目,添加GBase 8s的DLL引用,并提供了详细的C#代码示例,涵盖数据库连接、表的创建与修改、数据的增删查改等操作,旨在帮助开发者提高数据库管理效率。
|
1月前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
41 1