ADO.NET数据连接池
来源:互联网 发布:淘宝买小电影怎么搜索 编辑:程序博客网 时间:2024/05/01 21:51
21世纪什么最贵?数据库连接。对于以
1、什么是连接池
连接池是Data Provider提供的一个机制,使得应用程序使用的连接保存在连接池里而避免每次都要完成建立/关闭连接的完整
Data Provider在收到连接请求时建立连接的完整过程是:先连接池里建立新的连接(即“逻辑连接”),然后建立该“逻辑连接”对应的“物理连接”。建立“逻辑连接”一定伴随着建立“物理连接”。Data Provider关闭一个连接的完整过程是先关闭“逻辑连接”对应的“物理连接”然后销毁“逻辑连接”。销毁“逻辑连接”一定伴随着关闭“物理连接”。SqlConnection.Open()是向Data Provider请求一个连接,Data Provider不一定需要完成建立连接的完整过程,可能只需要从连接池里取出一个可用的连接就可以;SqlConnection.Close()是请求关闭一个连接,Data Provider不一定需要完成关闭连接的完整过程,可能只需要把连接释放回连接池就可以。
SqlConnection con = new SqlConnection("server = .;database = northwind;pooling = false;trusted_connection = true");for(int i = 0;i < 10;i++) { try { con.Open(); System.Threading.Thread.Sleep(1000); } catch(Exception e){Console.WriteLine(e.Message);} finally { con.Close(); System.Threading.Thread.Sleep(1000); } }
首先,不使用连接池做测试。以上
图1
下面启用连接池再
从下图可以看出,从第一次Open到第执行完Console.Read()这段时间内,SQL Server的“物理连接”数量一直保持为1,直到关闭console应用程序的进程后SQL Server的“物理连接”数量才变为0。由于使用了连接池,每次Close连接的时候Data Provider只需把“逻辑连接”释放回连接池,对应的“物理连接”则保持打开的状态。每次Open连接的时候,Data Provider只需从连接池取出一个“逻辑连接”,这样就可以使用其对应“物理连接”而不需建立新的“物理连接”,直线图因此而成。
图2
在ADO.NET 1.1下使用性能计数器观察连接池有关计数器需要注意两个bug。
(1)当应用程序进程关闭后,计数器“SqlClient: Current # pooled connections”和“SqlClient: Current # connection pools”不会减为0,所以每重新运行一次应用程序性能计数器的值在上次的值的基础上一直累加。这是计数器的错误显示,实际上当应用程序关闭后connection pool和pooled connection就减为0。因为关闭应用程序后把性能监视器也关闭,重启应用程序后再重新打开性能监视器就可以看出“SqlClient: Current # pooled connections”和“SqlClient: Current # connection pools”是重新从0开始
(2)用断点调试的情况下,连接串为"server = .;database = northwind;pooling = true;trusted_connection = true" 的connnection第一次Open的时候“SqlClient: Current # pooled connections”就从0变为2。但
为什么需要连接池?
完成建立/关闭一个连接的完整过程是一个消耗
Data Provider提供连接池并通过连接池实现“物理连接”重复使用而避免频繁地建立和关闭“物理连接”,从而大大提高应用系统的性能。图1描述一个应用的不同Client App使用连接池访问数据库,Data Provider负责建立和管理一个或者多个的连接池,每一个连接池里有一个或者多个连接,池里的连接就是“逻辑连接”。连接池里有N个连接表示该连接池与数据库之间有N个“物理连接”。增加一个连接,连接池与数据库的“物理连接”就增加一个,减少一个连接,连接池与数据库的“物理连接”就减少一个。
图3
Data Provider为每个进程管理该进程的连接池,一个进程可以有一个或者多个连接池。Data Provider是
笔者看过有些
如果你需要相同的连接串,首先你保证两个连接串每一个字符都相同,但这还不能保证Data Provider只为你建立一个连接池。因为如果你使用Windows
连接池建立后直到它所属的进程结束才会被销毁。
3、一个连接池里有多少个连接
明白了怎么区分不同的连接池后,下面我们来看看一个连接池里有多少个连接。一个连接池里的连接数不是静态的数量,它会随着连接池的不同状态而改变。这就涉及连接池建立的时候有多少个连接,什么时候连接会减少,什么时候会增加,连接数的上限是多少等问题。
首先来看看能
3.1增加连接
一旦连接池被建立,就立即建立由Min Pool Size
如果Max Pool Size已经达到而且所有连接都被占用,新的连接请求需要等待。如果有被占用的连接释放回连接池,那么请求得到该连接;如果请求等待超过Connection Timeout的
3.2减少连接
两种情况下连接池里的连接会减少。
(1)每当一个连接使用完后释放回连接池,如果当前时间减去该连接建立的时间的值大于Connection Lifetime设定的值(秒),该连接被销毁。Connection Lifetime是用于集群数据库环境下。例如一个应用
注意:Connection Lifetime很容易让人产生误解。不要认为Connection Lifetime决定了一个连接的生存时间。因为只有连接被释放回连接池的时刻(Close连接之后)才会检查Connection Lifetime值是否达到而决定是否销毁连接,而连接在空闲或者正在使用的时候并不会检查Connection Lifetime。这意味着绝大多数情况下连接从建立到销毁经过的时间比Connection Lifetime大。另外,如果Min Pool Size为N (N > 0),那么连接池里有N个连接不受Connection Lifetime影响。这N个连接会一直在池里直到连接池被销毁。
(2)当发现某个连接对应的“物理连接”断开(这种连接称为“死连接”),例如数据库已经被shutdown、网络中断、SQL Server的连接进程被kill、Oracle的连接会话被kill,该连接被销毁。“死连接”出现后不是立刻被发现,直到该连接被占用来访问数据库的时候才会被发现。
注意:如果执行Open()
下面以一个例子详细解释一个连接池从建立起到进程结束连接数的变化情况。
string connectionString = "server = .;database = northwind;user = sa;password = sqlserver;min pool size = 2;max pool size = 5; connection lifetime = 20;connection timeout = 10";SqlConnection[] connections = new SqlConnection[7]; for(int i = 0;i < connections.Length;i++) connections[i] = new SqlConnection(connectionString); …Open connection[0],8秒后Open connection[1] …8秒后Close connection[0],10秒后Open connection[0] …5秒后Open connection[2]、[3]、[4],每隔两秒打开一个 Console.WriteLine("Now the Max Pool Size is reached and we try to open connection[5]./r/n"); for(int i = 0;i < 2;i++) { try {connections[5].Open();} catch(InvalidOperationException e) { if(i == 1) return; Console.WriteLine("Can't open connection[5]./r/n" + e.Message); connections[4].Close(); Console.WriteLine("/r/nTry to open connection[5] again."); continue; } } Console.WriteLine("connection[5] is open."); foreach(SqlConnection con in connections) { if(con.State == ConnectionState.Open) { con.Close(); Console.WriteLine("A connection is released back to the pool."); System.Threading.Thread.Sleep(5000); } }
使用性能监视器观察,得到图4所示
图4
由于Min Pool Size = 2,所以open connection[0]的时候连接池里就建立了两个连接。之后open connection[1]、close connection[0]、open connection[0]这段时间里连接池连接数
另外强调两点:
(1)可用看出增加/减少一个连接池的连接,User Connections(即“物理连接”)随着增加/减少一个。(为方便观察,先用Sql Query Analyzer打开一个用户连接)
(2)由于使用相同连接串,所以由始至终只有一个连接池。
4、连接泄漏
前面说过,连接被打开后需要执行Close或者Dispose方法后才会释放回连接池。如果一个连接已经离开其代码有效范围,但还没被Close或者Dispose,该连接就被泄漏了。所谓泄漏的连接就是代码中已经不再使用某个连接但该连接却还没有被释放回连接池。下面代码中,每执行一次Method()就泄漏一个连接,第11次执行的时候就会抛出InvalidOperationException,因为最大连接数已达而且所有连接都已经被占用。
private void Method(){ string conString = "server = .;database = northwind;user = sa; password = sqlserver;max pool size = 10"; SqlConnection con = new SqlConnection(conString); con.Open();}
如果一个应用
要避免连接的泄漏,请注意下面几点:
(1)除非使用CommandBehavior.CloseConnection作ExecuteReader参数,否则Close DataReader不会Close关联的连接。在多层结构的系统中,如果中间层向表现层返回DataReader,那么必须使用CommandBehavior.CloseConnection作ExecuteReader参数,这样当表现层执行DataReader的Close方法时就会Close连接,不然表现层想帮你也有心无力。
(2)执行DataAdapter的Fill和Update方法时,如果连接没有打开,那么DataAdapter自动会打开连接,执行完操作后自动关闭连接;但如果连接已经打开,DataAdapter执行完操作后不会帮你关闭连接,你需要自己负责关闭连接。
5、处理“死连接”
“可用的”连接一定能访问数据库?不一定。
在前面“减少连接”的部分提过,在数据库被shutdown、网络中断、数据库连接进程/会话被kill
“死连接”是“逻辑连接”,是“可用的”连接,但该“逻辑连接”对应的“物理连接”已经不存在;泄漏的连接指“物理连接”存在而对应的“逻辑连接”实际没有被占有但被标识为“被占用”而导致该“逻辑连接”不能被使用。
发现“死连接”后Data Provider会销毁该连接并抛出SqlException但不会自动尝试使用其他连接,即使在ADO.NET 2.0里也是如此。把exception catch下来,然后
下面例子里Helper的ExecuteReader把Data Provider抛出的SqlException catch后先把连接置为“无效”,然后再尝试使用其他连接,如果再尝试的次数达到预定值还不
public class Helper { private static int TimesTry = 0,MaxTry = 5; public static SqlDataReader ExecuteReader(string conStr,CommandType eType, string commandText) { SqlConnection cn = null; SqlDataReader dr = null; SqlCommand cmd = null; try { cn = new SqlConnection(conStr); cmd = new SqlCommand(commandText,cn); cmd.CommandType = eType; cn.Open(); dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); } catch(SqlException e) { if(dr != null) dr.Close(); cn.Close(); System.Threading.Thread.Sleep(2000); if(TimesTry < MaxTry) { dr = ExecuteReader(conStr,eType,commandText); TimesTry++; } else throw e; } return dr; } } string conString = "server = .;database = northwind; user = sa;max pool size = 1;password = sqlserver; Application Name = DeadConnectionExample"; SqlDataReader reader = Helper.ExecuteReader(conString,CommandType.Text, "select * from orders"); reader.Close(); System.Threading.Thread.Sleep(15000); SqlConnection con = new SqlConnection("server = .;database = master; user = sa;password = sqlserver;pooling = false"); con.Open(); SqlCommand cmd = new SqlCommand("SELECT SPID FROM master.dbo.sysprocesses WHERE PROGRAM_NAME = 'DeadConnectionExample'",con); string spid = cmd.ExecuteScalar().ToString(); cmd = new SqlCommand("kill " + spid,con); cmd.ExecuteNonQuery(); con.Close(); System.Threading.Thread.Sleep(5000); reader = Helper.ExecuteReader(conString,CommandType.Text,"select * from orders"); reader.Close();
Main方法里,第一次调用Helper.ExecuteReader后建立了连接池并建立了一个连接,接着我们
图5中连接池数量一直
当然,如果“死连接”是由于网络中断、数据库被shutdown引起,那么Helper只能最后抛出SqlException。
注意:查询master.dbo.sysprocesses使用的连接串没有必要使用连接池。
图5
6、ADO.NET 2.0性能计数器
NumberOfFreeConnections、 NumberOfActiveConnections、 SoftDisconnectsPerSecond和SoftConnectsPerSecond默认在性能监视器是不打开的,要观察这些计数器的值需要在
<system.diagnostics><switches><add name="ConnectionPoolPerformanceCounterDetail"value="4"/> </switches></system.diagnostics>
NumberOfActiveConnectionPoolGroups计数器。前面说过,如果连接串使用Windows
NumberOfActiveConnections, NumberOfFreeConnections计数器。ADO.NET 1.1里的计数器没有提供一个连接池里的连接有多少个是“被占用的”,有多少个是“可用的”。NumberOfActiveConnections和NumberOfFreeConnections填补了这个空白。这两个计数器更加“生动”地描述了连接池里连接的变化情况。图6是一个连接相继Open/Close了4次得到的比ADO.NET 1.1更“生动”的曲线。
图6
7、总结
明白了连接池的运作机制不等于能正确使用连接池,要充分挖掘连接池给应用系统带来的性能提高,除了避免泄漏连接
(1)确保每次访问数据库使用相同的连接串,连接串不要使用Windows认证。
(2)到了非打开不可的时候才打开连接,连接使用完毕立刻关闭连接。因为过早占用和过晚释放连接意味着增加连接池的不必要负荷(需要建立更多的连接以及连接请求需要等待更长时间)。
(3)
(4)如果应用系统不是使用集群数据库,把Connection Lifetime设置为0。在单数据库服务器的环境下没必要把连接销毁,因为销毁后一段时间又需要建立。
连接池对应用系统的性能
- ADO.NET数据连接池
- ADO.NET数据连接池
- ADO.NET数据连接池
- ADO.NET数据连接池
- ADO.NET数据连接池剖析
- ADO.NET 数据连接查询
- ADO.NET连接池
- ADO.NET:连接池
- ADO.NET连接池
- ADO.net 连接池
- ADO.Net连接池
- ado.net 连接池
- ADO.NET连接池
- ADO.NET连接池
- ADO.NET连接池
- ADO.NET数据连接池的秘密【转载】
- ADO.NET 连接方式进行数据访问
- ADO.NET连接池FAQ
- linux 命令发送 邮件
- 好久没有写BLOG了,人老了就开始变懒了【非技术】
- Java笔试面试题整理第八波
- jQuery之动画效果
- 乐学成语之小游戏
- ADO.NET数据连接池
- 环境变量中系统变量和用户的环境变量及在配置JAVA环境变量时两着有什么区别
- spark mllib als推荐引擎学习
- Android控件学习之TextView:设置中划线,下划线效果
- 使用C/C++编写函数,实现字符串反转,要求不使用任何系统函数,且时间复杂度最小
- Scrollview嵌套listView数据显示不正常问题
- Ubuntu15.10安装Django教程
- Android下pm 命令详解 - 安装APK
- SAP通过程序创建Excel模板