ADO.NET数据连接池
来源:互联网 发布:软件怎么创建快捷方式 编辑:程序博客网 时间:2024/04/28 13:14
来源:http://www.qqread.com/aspdotnet/n372430.html
21世纪什么最贵?数据库连接
1、什么是连接池
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。
为什么需要连接池?
Data Provider提供连接池并通过连接池实现“物理连接”重复使用而避免频繁地建立和关闭“物理连接”,从而大大提高应用系统的性能。图1描述一个应用的不同Client App使用连接池访问数据库,Data Provider负责建立和管理一个或者多个的连接池,每一个连接池里有一个或者多个连接,池里的连接就是“逻辑连接”。连接池里有N个连接表示该连接池与数据库之间有N个“物理连接”。增加一个连接,连接池与数据库的“物理连接”就增加一个,减少一个连接,连接池与数据库的“物理连接”就减少一个。
图3
Data Provider为每个进程管理
如果你需要相同的连接串,首先你保证两个连接串每一个字符都相同,但这还不能保证Data Provider只为你建立一个连接池。因为如果你使用Windows
连接池建立后直到它所属的进程结束才会被销毁。
3、一个连接池里有多少个连接
明白了怎么区分不同的连接池后,下面我们来看看一个连接池里有多少个连接。一个连接池里的连接数不是静态的数量,它会随着连接池的不同状态而改变。这就涉及连接池建立的时候有多少个连接,什么时候连接会减少,什么时候会增加,连接数的上限是多少等问题。
3.1增加连接
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,该连接被销毁。“死连接”出现后不是立刻被发现,直到该连接被占用来访问数据库的时候才会被发现。
注意:
下面以一个例子详细解释一个连接池从建立起到进程结束连接数的变化情况。
string connectionString = "server = .;database = northwind;user = sa;password = sqlserver;min pool size = 2;max pool size = 5;SqlConnection[] connections = new SqlConnection[7];
connection lifetime = 20;connection timeout = 10";
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
另外强调两点:
(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、处理“死连接”
“可用的”连接一定能访问数据库?不一定。
“死连接”是“逻辑连接”,是“可用的”连接,但该“逻辑连接”对应的“物理连接”已经不存在;泄漏的连接指“物理连接”存在而对应的“逻辑连接”实际没有被占有但被标识为“被占用”而导致该“逻辑连接”不能被使用。
发现“死连接”后Data Provider会销毁该连接并抛出SqlException但不会自动尝试使用其他连接,即使在ADO.NET
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();
当然,如果“死连接”是由于网络中断、数据库被shutdown引起,那么Helper只能最后抛出SqlException。
注意:查询master.dbo.sysprocesses使用的连接串没有必要使用连接池。
图5
6、ADO.NET 2.0性能计数器
<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)到了非打开不可的时候才打开连接,连接使用完毕立刻关闭连接。因为过早占用和过晚释放连接意味着增加连接池的不必要负荷(需要建立更多的连接以及连接请求需要等待更长时间)。
(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
- jdbc桥连接数据库练习【1】
- SCJP认证套题解析之一
- 要分专业了
- 用 Python 的输入输出功能读取和写入数据
- Heritrix的多线程ToeThread和ToePool
- ADO.NET数据连接池
- 我的人生感悟
- SCJP认证套题解析之二
- SCJP认证套题解析之三
- 格式化输出数字
- 类注释文档编写方法
- SqlDataReader,DataSet判空总结
- 个人简介
- 如何防止IE缓存jsp文件