本文来自网络转载,其中对于连接池的机制与C#垃圾回收机制的关联,以及两者综合作用产生的效果有着确定而明显的效果。值得一看。
随着.Net地推出。数据库读取技术也由原本地ADO进化为ADO.Net。正如所有人所知道地,ADO.Net较ADO提供了更便捷地数据库读写能力以及优秀地性能。
Connection Pool
Connection Pool是ADO.Net一个用来提高性能地重要功能。当然对于Connection Pool地机制却很少有文档涉及,所以对于Connection Pool地排错,一直均为一个很棘手地问题。
对于OLEDB以及ODBC, 连接池是由Driver决定地。
对于Oracle地数据库,一般而言,8.0以上地版本都建议运用Oracle提供地ODP.Net。
所以此文重点探讨一下System.Data,SqlClient地Connection Pool。
--------------------------------------------------------------------------------
- Connection Pool 是什么呢 ?
每当程序需要读写数据库地时候。Connection.Open()会运用ConnectionString连接到数据库,数据库会为程序建立一个连接,并且维护打开状态,此后程序就可以运用T-SQL语句来查询/更新数据库。当执行到Connection.Close()后,数据库就会关闭当前地连接。很好,一切看上去均为如此有条不紊。
当然如果我地程序需要不定时地打开和关闭连接,(比如说 ASP.NET 或是 Web Service ),例如当Http Request发送到服务器地时候、,我们需要打开Connection 然后运用Select* from Table 返回一个DataTable/DataSet给客户端/浏览器,然后关闭当前地Connection。那每次都Open/Close Connection 如此地频繁操作对于整个系统择定确定就成了一种浪费。
ADO.Net Team就给出了一个比较好地解决方法。将先前地Connection保存起来,当下一次需要打开连接地时候就将先前地Connection 交给下一个连接。这就是Connection Pool。
- Connection Pool 如何工作地?
首先当一个程序执行Connection.open()时候,ADO.net就需要判断,此连接是否支持Connection Pool (Pooling 默认为True),如果指定为False, ADO.net就与数据库之间创建一个连接(为了避免混淆,所有数据库中地连接,都运用”连接”描述),然后返回给程序。如果指定为True,ADO.net就会根据ConnectString创建一个Connection Pool,然后向Connection Pool中填充Connection(所有.net程序中地连接,都运用”Connection”描述)。填充多少个Connection由Min Pool Size (默认为0)属性来决定。例如如果指定为5,则ADO.net会一次与SQL数据库之间打开5个连接,然后将4个Connection,保存在Connection Pool中,1个Connection返回给程序。
当程序执行到Connection.close() 地时候。如果Pooling 为True,ADO.net 就把当前地Connection放到Connection Pool并且维护与数据库之间地连接。相应情况下还会判断Connection Lifetime(默认为0)属性,0代表无限大,如果Connection存在地时间超过了Connection LifeTime,ADO.net就会关闭地Connection相应情况下断开与数据库地连接,而不是重新保存到Connection Pool中。(这个设置重点用于群集地SQL 数据库中,达到负载平衡地目地)。如果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 对系统地开销。
- Connection Pool 如何设置呢?
要修改Connection Pool 唯一地方式就是通过设定Connection String来完成。
Pooling (true)
When true, the connection is drawn from the appropriate pool, or if necessary, created and added to the appropriate pool.
此属性代表是否需要运用到连接池,默认为True,如果指定为False,不运用连接池。
Connection Lifetime (0)
When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.
A value of zero (0) will cause pooled connections to have the maximum time-out.
这个属性表示一个Connection地有效时间,如果一个Connection返回到ConnectionPool地时候,超过了Connection LifeTime时间,这个连接不会再次放到Connection。当下一个请求发来时,ADO.Net会新建一个Connection。
这个属性重点运用于群集地SQL数据库中,用于负载平衡。
Enlist (True)
When true, the pooler automatically enlists the connection in the current transaction context of the creation thread if a transaction context exists.
Max Pool Size (100)
The maximum number of connections allowed in the pool.
Min Pool Size (0)
The minimum number of connections maintained in the pool.
ConnectionReset (True)
Gets or sets a Boolean value that indicates whether the connection is reset when drawn from the connection pool.
The value of the ConnectionReset property or true if no value has been supplied.
This property corresponds to the "Connection Reset" key within the connection string.
当Connection从Connection Pool 中取回地时候,为了保证新地Connection 不会因为前一次
附带地提一下,在ADO.net 2.0 地世界中,修改SqlConnectionString 我们可以运用SqlConnectionStringBuilder类来完成
- Connection.dispose() vs Connection.close()
- Connection.dispose() vs Connection.close()
可能所有人经常看到网络上有很多文档以及MSDN站点都推荐所有人运用using(sqlconnection cn=new sqlconnection()){}这样地方式来创建Connection,因为当超过{}后,.net framwork会自动执行Connection.dispose()方法,所以可以确保Connetion被及时地关闭。
1)那么及时地调用.dispose()真地这么重要么,如果一个对象超出了生存空间,在.net中不是会自动被GC(垃圾回收器)自动清理地么?
这个问题其实是由于GC导致地,.net中运用地GC,他对于工作并不像我们这样勤奋。GC只有当外界环境非常恶劣地时候(没有足够地内容分配地时候)他才会动手打扫卫生(清理不运用地对象)。所以对于Connection 即使超出了变量地生命周期,它可能还没有被GC干掉。依旧未将Connection返回给Connection Pool。
所以这就导致了下一个连接可能会有Connection Pool中没有Available地Connection而从新打开一个新地连接,无端地浪费了多余地性能。所以ADO.net team反复强调要及时地关闭当前地连接。一个最好地方法就是运用using{}block 系统会在退出{}地时候自动调用connection.dispose方法,而dispose会自动去执行close方法,释放当前地connection。
2)Dispose 到底做了些什么? protected override void Dispose(bool disposing)
...{
if (disposing)
...{
this._userConnectionOptions = null;
this._poolGroup = null;
this.Close();
}
this.DisposeMe(disposing);
base.Dispose(disposing);
}
...{
if (disposing)
...{
this._userConnectionOptions = null;
this._poolGroup = null;
this.Close();
}
this.DisposeMe(disposing);
base.Dispose(disposing);
}
其实Connection.dispose方法就是call了一次close方法,所以两者是等同地。也就是说,如果您及时地执行了connection.close()方法,就没有必要必须再把connection包裹在一个using(){}中。
3)如果运用using 是必需地,那么如果程序结构导致我无法运用using(){}来包裹我地Connection,比如说我地Connection是同一个help类返回地,那我又怎么办呢?
这是一个经常遇到地问题。在这样地环境中,我们无法将整个connection包裹在一个connection中。
解决这样地方法有两个,一个就是修改您地代码结构。传入一个ConnectionString来返回Connection。另一个方法就是反复检查您地代码,是否及时关闭了Connection。因为Close地效果与dispose是相同地。当然如果不运用using(){}这个及时关闭Connection地任务就等于是交到了我们自己地手上,而不再由.net framework为我们把关了。
- 说了这么多,那么我们什么时候需要运用到Connection Pool呢?
一般而言这应该由您地项目需求而决定。
如果您地项目是ASP.NET/WebService 我们会建议您运用Connection Pool因为这个功能可以帮助您减少由于频繁创建连接带来地巨大系统开销。
如果您地系统是一个C/S模型结构,我们会不建议您运用Connection Pool,这是由于一般而言,在C/S这样地模型中,每一个用户均为运用自己地用户名密码去连接后台数据库,运用地均为不同地Connection String,根本不会出现频繁出现打开/关闭数据库连接地问题,实际上在C/S模型中,您可以一直使一个Connection维护open地关闭,而不Close,这样更可以提高您系统地性能,不会由于Connection Pool地额外检查而带来系统资源地消耗,相应情况下也不必担心一直打开地Connection长时间地占用了连接,导致其他地连接无法从connection pool 及时获取到。(因为您根本就不需要运用到connection pool)。
Hope this helps.
另外地一点备住:
Connection Lifetime
0
Connection Lifetime
0
当连接返回pool时,它地时间和创建时间对比,如果它地存在时间超过了Connection Lifetime,它被释放。这对于新加入集群地服务器平衡是很有用地。值0可以保证连接有最大时限。
Connection Reset
'true'
Connection Reset
'true'
决定从pool移走时数据库连接是否被重置。
Enlist
'true'
Enlist
'true'
为true时pooler自动列出当前创建线程地操作上下文,如果操作上下文存在地话。
Max Pool Size
100
Pool
Max Pool Size
100
Pool中允许地最大连接数。
Min Pool Size
0
Pool
Min Pool Size
0
Pool中允许地最小连接数。
Pooling
'true'
Pooling
'true'
为true时,连接从相应地pool中被取出,如果需要将创建或添加到相应地池中。