1. 什么是连接池?

  我们都知道,建立一个数据库连接是一件非常耗时(消耗时间)耗力(消耗资源)的事情。之所以会这样,是因为连接到数据库服务器需要经历几个漫长的过程:建立物理通道(例如套接字或命名管道),与服务器进行初次握手,分析连接字符串信息,由服务器对连接进行身份验证,运行检查以便在当前事务中登记等等。既然新建一条连接如此痛苦,那么为什么不重复利用已有的连接呢?

实际上,ADO.NET已经为我们提供了名为连接池的优化方法。连接池就是这样一个容器:它存放了一定数量的与数据库服务器的物理连接。因此,当我们需要连接数据库服务器的时候,只需去池(容器)中取出一条空闲的连接,而不是新建一条连接。这样的话,我们就可以大大减少连接数据库的开销,从而提高了应用程序的性能。

2. 连接池的工作原理

2.1 创建连接池

注意,连接池是具有类别区分的。即,同一个时刻同一应用程序域可以有多个不同类型的连接池。那么,连接池是由进程、应用程序域、连接字符串以及windows标识(在使用集成的安全性时)共同组成签名来标识区分的。但对于同一应用程序域来说,一般只由连接字符串来标识区分。当打开一条连接时,如果该条连接的类型签名与现有的连接池类型不匹配,则创建一个新的连接池。反之,则不创建新的连接池。

一个典型的创建连接的实例:

//创建连接对象1
using (SqlConnection conn1 = new SqlConnection("DataSource=(local);Integrated Security=SSPI;Initial Catalog=Northwind"))
{
conn1.Open();
} //创建连接对象2
using (SqlConnection conn2 = new SqlConnection("DataSource=(local);Integrated Security=SSPI;Initial Catalog=pubs"))
{
conn2.Open();
} //创建连接对象3
using (SqlConnection conn3 = new SqlConnection("DataSource=(local);Integrated Security=SSPI;Initial Catalog=Northwind"))
{
conn3.Open();
}

  上面实例中,我创建了三个SqlConnection对象,但是管理时只需要两个连接池。可以看到conn1与conn3的连接字符串相同,所以可以共享一个连接池,而conn2与conn1、conn3不同,所以需要创建新的连接池。

2.2 分配空闲连接

当用户创建连接请求或者说调用Connection对象的Open时,连接池管理器首先需要根据连接请求的类型签名找到匹配类型的连接池,然后尽力分配一条空闲连接。具体情况如下:

  [1]如果池中有空闲连接可用,返回该连接。

  [2]如果池中连接都已用完,创建一个新连接添加到池中。

  [3]如果池中连接已达到最大连接数,请求进入等待队列直到有空闲连接可用。

2.3 移除无效连接

无效连接,即不能正确连接到数据库服务器的连接。对于连接池来说,存储的与数据库服务器的连接的数量是有限的。因此,对于无效连接,如果如不及时移除,将会浪费连接池的空间。其实你不用担心,连接池管理器已经很好的为我们处理了这些问题。如果连接长时间空闲,或检测到与服务器的连接已断开,连接池管理器会将该连接从池中移除。

2.4 回收使用完的连接

当我们使用完一条连接时,应当及时关闭或释放连接, 连接在关闭或断开时释放回池中,以便重复利用。我们可以通过Connection对象的Close或Dispose方法,也可以通过C#的using语句来关闭连接。

2.5 Connection Pool 如何工作

  首先当一个程序执行Connection.open()时候,ADO.Net 就需要判断,此连接是否支持Connection Pool (Pooling 默认为True),如果指定为False, ADO.Net就与数据库之间创建一个连接,然后返回给程序。

  如果指定为 True,ADO.Net就会根据ConnectString创建一个Connection Pool,然后向Connection Pool中填充Connection。填充多少个Connection由Min Pool Size (默认为0)属性来决定。例如如果指定为5,则ADO.Net会一次与SQL数据库之间打开5个连接,然后将4个Connection,保存在 Connection Pool中,1个Connection返回给程序。

  当程序执行到Connection.close() 的时候:
  [1] 如果Pooling 为True,ADO.Net 就把当前的Connection放到Connection Pool并且保持与数据库之间的连接。

  同时还会判断Connection Lifetime(默认为0)属性,0代表无限大,如果Connection存在的时间超过了Connection LifeTime,ADO.net就会关闭的Connection同时断开与数据库的连接,而不是重新保存到Connection Pool中。(这个设置主要用于群集的SQL 数据库中,达到负载平衡的目的)。

  [2] 如果Pooling指定为False,则直接断开与数据库之间的连接。

  然后当下一次Connection.Open() 执行的时候,ADO.Net就会判断新的ConnectionString与之前保存在Connection Pool中的Connection的connectionString是否一致。(ADO.Net会将ConnectionString转成二进制流,也就是说,新的ConnectionString与保存在Connection Pool中的Connection的ConnectionString必须完全一致,即使多加了一个空格,或是修改了Connection String中某些属性的次序都会让ADO.Net认为这是一个新的连接,而从新创建一个新的连接。所以如果您使用的UserID,Password的认证方式,修改了Password也会导致一个Connection,如果使用的是SQL的集成认证,就需要保存两个连接使用的是同一个)。

  然后 ADO.net需要判断当前的Connection Pool中是否有可以使用的Connection(没有被其他程序所占用),如果没有的话,ADO.net就需要判断ConnectionString设 置的Max Pool Size (默认为100),如果Connection Pool中的所有Connection没有达到Max Pool Size,ADO.net则会再次连接数据库,创建一个连接,然后将Connection返回给程序。

  如果已经达到了 MaxPoolSize,ADO.net就不会再次创建任何新的连接,而是等待Connection Pool中被其他程序所占用的Connection释放,这个等待时间受SqlConnection.ConnectionTimeout(默认是15 秒)限制,也就是说如果时间超过了15秒,SqlConnection就会抛出超时错误(所以有时候如果SqlConnection.open()方法抛 出超时错误,一个可能的原因就是没有及时将之前的Connnection关闭,同时Connection Pool数量达到了MaxPoolSize。)

  如果有可用的Connection,从Connection Pool 取出的Connection也不是直接就返回给程序,ADO.Net 还需要检查ConnectionString的ConnectionReset属性 (默认为True)是否需要对Connection 进行一次reset。由于,之前从程序中返回的Connection可能已经被修改过,比如说使用 SqlConnection.ChangeDatabase method 修改当前的连接,此时返回的 Connection 可能就已经不是连接当前的Connection String指定的Initial Catalog数据库了。所以需要reset一次当前的连接。但是由于所有的额外检查都会增大ADO.Net Connection Pool 对系统的开销。

3. 说说几个非常重要属性

连接池的行为可以通过连接字符串来控制,主要包括四个重要的属性:

  [1]Connection Timeout:以秒为单位的连接生存期最大值(默认值是15 秒;0 表示无限制连接永不失效) ;
  [2]Connection Reset:是否自支重置来自连接池中的连接(默认值是True);
  [3]Enlist:是否将连接置入当前事务的上下文中(默认是True);
  [4]Load Balance Timeout:同Connection Timeout属性;
  [5]Max Pool Size:保存在连接池中的最大连接数(默认值是100,理论最大值为32767;N,来动态扩大连接池中的连接最大数量);
  [6]Min Pool Size:保存在连接池中最小的连接数(默认值是0,建议调整为5);

  如果 MinPoolSize 在连接字符串中未指定或指定为零,池中的连接将在一段时间不活动后关闭。但是,如果指定的 MinPoolSize 大于零,在 AppDomain 被卸载并且进程结束之前,连接池不会被破坏。非活动或空池的维护只需要最少的系统开销。注意,当出现故障转移等错误时,会自动清除池。
  [7]Pooling:是否启用连池功能(默认为True)。ADO.NET默认是启用连接池的,因此,若要禁用需手动设置Pooling=false来禁用连接池。

  [8]Connection Lifetime:Connection的生存周期(默认为0,无限大)

  实例来理解连接池的属性吧。代码如下:

SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
connStr.DataSource = @".\SQLEXPRESS";
connStr.InitialCatalog = "master";
connStr.IntegratedSecurity = true; connStr.Pooling = true; //开启连接池
connStr.MinPoolSize = ; //设置最小连接数为0
connStr.MaxPoolSize = ; //设置最大连接数为50
connStr.ConnectTimeout = ; //设置超时时间为10秒    using( SqlConnection conn = new SqlConnection(connStr.ConnectionString))
{
//todo
}
<connectionStrings>
<add name="DBConnection" connectionString="Min Pool Size=10;Max Pool Size=100;Connection Timeout=10;DataSource=.;
    DataBases=System;ID=sa;Pwd=sa"/>
</connectionStrings>

4. 连接池异常与处理方法

当用户打开一个连接而没有正确或者及时的关闭时,经常会引发“连接泄露”问题。泄露的连接,会一直保持打开状态,直到调用Dispose方法,垃圾回收器(GC)才关闭和释放连接。与ADO不同,ADO.NET需要手动的关闭使用完的连接。一个重要的误区是:当连接对象超出局部作用域范围时,就会关闭连接。实际上,当超出作用域时,释放的只是连接对象而非连接资源。

  实例如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient; namespace ConnectionPool
{
class Program
{
static void Main(string[] args)
{
SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
connStr.DataSource = @".\SQLEXPRESS";
connStr.InitialCatalog = "master";
connStr.IntegratedSecurity = true; connStr.MaxPoolSize = ;//设置最大连接池为5
connStr.ConnectTimeout = ;//设置超时时间为1秒 SqlConnection conn = null;
for (int i = ; i <= ; ++i)
{
conn = new SqlConnection(connStr.ConnectionString);
try
{
conn.Open();
Console.WriteLine("Connection{0} is linked",i);
}
catch(Exception ex)
{
Console.WriteLine("\n异常信息:\n{0}",ex.Message);
break;
}
}
Console.Read();
}
}
}

  使结果更明显,特地将最大连接数设置为5,超时时间为1秒。运行后,很快得到以下结果。

  

  从上面的结果我们很明显的知道,连接出现了异常。我们已经知道连接池的最大连接数为5,当创建第6条连接时,由于连接池中连接数量已经达到了最大数并且没有空闲的连接,因此需要等待连接直到超时。当超过超时时间时,就出现了上述的连接异常。因此,必须再次强调,使用完的连接应当尽快的正确的关闭和释放。

5. 监视SQL Server连接状态的方法

(1)通过活动监视器

  第一步:打开MSSMS管理器,单击“活动监视器”图标。

  

  第二步:在打开活动监视器视图中,单击“进程”选项卡。

  

  第三步:运行 #4 连接池异常与处理方法 中的例子,则可以看到打开的5条连接,如下图所示。

  

  (2)使用T-SQL语句

  [1] 通过执行系统存储过程sp_who,我们也可以监视连接状态。

exec sp_who

  可得到以下结果:

  

  [2] 通过 master 数据库的 sys.sysprocesses 系统视图查询相关信息。

---查询指定字段---
SELECT spid,ecid,status,loginame=rtrim(loginame),hostname,blk=convert(char(5),blocked),
dbname = CASE WHEN dbid = 0 THEN null WHEN dbid <> 0 THEN db_name(dbid) END
,cmd,request_id
FROM sys.sysprocesses WHERE dbid=db_id('数据库名') ---查询全部字段---
SELECT * FROM sys.sysprocesses WHERE dbid=db_id('数据库名')

6. 高效使用连接池的基本原则

用好连接池将会大大提高应用程序的性能。相反,如果使用不当的话,则百害而无一益。一般来说,应当遵循以下原则:

  [1]在最晚的时刻申请连接,在最早的时候释放连接。

  [2]关闭连接时先关闭相关用户定义的事务。

  [3]确保并维持连接池中至少有一个打开的连接。

  [4]尽力避免池碎片的产生。主要包括集成安全性产生的池碎片以及使用许多数据库产生的池碎片。

  提示:池碎片是许多 Web 应用程序中的一个常见问题,应用程序可能会创建大量在进程退出后才会释放的池。 这样,将打开大量的连接,占用许多内存,从而导致性能降低。

ADO.Net 之 数据库连接池(一)的更多相关文章

  1. ADO.Net 之 数据库连接池(二)

    连接到数据库服务器通常由几个需要很长时间的步骤组成.必须建立物理通道(例如套接字或命名管道),必须与服务器进行初次握手,必须分析连接字符串信息,必须由服务器对连接进行身份验证,必须运行检查以便在当前事 ...

  2. 必须知道的ADO.NET 数据库连接池

    http://www.cnblogs.com/liuhaorain/archive/2012/02/19/2353110.html 题外话 通过前几章的学习,不知道大家对ADO.NET有一定的了解了没 ...

  3. ADO.NET中SQL Server数据库连接池

    连接到数据库服务器通常由几个需要很长时间的步骤组成. 必须建立物理通道(例如套接字或命名管道),必须与服务器进行初次握手,必须分析连接字符串信息,必须由服务器对连接进行身份验证,必须运行检查以便在当前 ...

  4. ADO.NET入门教程(五) 细说数据库连接池

    摘要 今天我要讲的是数据库连接池.说实话,我表示鸭梨很大.因为相比其他章节来说,连接池相对来说难理解一点.我要用最通俗的语句给大家讲明白,讲透彻却也不是一件很容易的事.但是,连接池又是非常重要的知识点 ...

  5. C# ADO.net 数据库连接池

    前一阵开发一套系统,同组的同事提供了一个数据库连接组件,是他自己封装的,使用了自定义的连接池,用着很是不爽,而且经常会因为程序不严谨的原因,导致连接池里的连接被用完,也导致其他错误,因此我想自己研究一 ...

  6. 从零开始学 Java - 数据库连接池的选择 Druid

    我先说说数据库连接 数据库大家都不陌生,从名字就能看出来它是「存放数据的仓库」,那我们怎么去「仓库」取东西呢?当然需要钥匙啦!这就是我们的数据库用户名.密码了,然后我们就可以打开门去任意的存取东西了. ...

  7. 数据库连接池的选择 Druid

    我先说说数据库连接 数据库大家都不陌生,从名字就能看出来它是「存放数据的仓库」,那我们怎么去「仓库」取东西呢?当然需要钥匙啦!这就是我们的数据库用户名.密码了,然后我们就可以打开门去任意的存取东西了. ...

  8. paip.提升性能----数据库连接池以及线程池以及对象池

    paip.提升性能----数据库连接池以及线程池以及对象池 目录:数据库连接池c3po,线程池ExecutorService:Jakartacommons-pool对象池 作者Attilax  艾龙, ...

  9. 使用c#数据库连接池

    摘自: http://www.wxphp.com/wxd_0fetn2bw2548fsc2ak8h_1.html 导读:使用C#数据库连接池,连接到数据库服务器通常由几个需要软长时间的步骤组成,必须与 ...

随机推荐

  1. SqlSugar轻量ORM

      蓝灯软件数据股份有限公司项目,代码开源. SqlSugar是一款轻量级的MSSQL ORM ,除了具有媲美ADO的性能外还具有和EF相似简单易用的语法. 学习列表 0.功能更新 1.SqlSuga ...

  2. C#中如何截取Windows消息来触发自定义事件

    原文 C#中如何截取Windows消息来触发自定义事件 在c#windows开发中,我们常常会遇到拦截windows消息,来触发某个特定任务的问题. 由于目前使用c#的开发人员非常多,而且大多数c#程 ...

  3. 使用C语言实现二维,三维绘图算法(3)-简单的二维分形

    使用C语言实现二维,三维绘图算法(3)-简单的二维分形 ---- 引言---- 每次使用OpenGL或DirectX写三维程序的时候, 都有一种隔靴搔痒的感觉, 对于内部的三维算法的实现不甚了解. 其 ...

  4. 3.1 全局存储带宽与合并访问 -- Global Memory(DRAM) bandwidth and memory coalesce

    全局存储带宽(DRAM) 全局内存是动态随机访问的方式访问内存.我们希望访问DRAM的时候非常快,实际情况是DRAM中出来的数据非常非常慢,这就好比,理想状态是泄洪,水倾巢而出,气势宏伟,实际取水却像 ...

  5. HIbernate学习笔记(九) hibernate事务并发处理与乐观悲观锁

    事务并发处理 一. 数据库的隔离级别:并发性作用. 1.   ReadUncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到.)很少用 2.   ...

  6. AIDL实例

    转载声明:原文转自:http://www.cnblogs.com/xiezie/p/5658372.html 什么是AIDL Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进 ...

  7. HDU-4704 Sum 大数幂取模

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4704 题意:求a^n%m的结果,其中n为大数. S(1)+S(2)+...+S(N)等于2^(n-1) ...

  8. centos修改时区,设置时间

    在我们使用CentOS系统的时候,也许时区经常会出现问题,有时候改完之后还是会出错,下面我们就来学习一种方法来改变这个状况.如果没有安装,而你使用的是 CentOS系统 那使用命令 yum insta ...

  9. 【现代程序设计】【Homework-01】

    1维的最大子数组之和 对于1维的最大子数组之和 假设f[i]表示:对于1..i这个序列中,包含i这个元素的最大序列的值 则对于f[i],0<i<=n; 应该有 f[i]=max(a[i], ...

  10. 【转】可执行程序包括BSS段、数据段、代码段

    可执行程序包括BSS段.数据段.代码段(也称文本段). 一.BSS BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域.特点是:可 ...