Originator:Neeraj Saluja.
译者:吴秀祥(Tony Woo classweb#126.com)
Table of Contents
目录
和数据库建立连接是一项较重的耗费资源的过程。无论任何应用程序需要在任何数据库服务器上执行任意的查询语句,我们第一步都需要建立与数据库的连接。 If any application needs to fire any query against any database server, we need to first establish a connection with server and then execute the query against that database server.
不论你同意与否,当你在写存储过程或查询的语句时,这个查询返回结果的响应时间一定要比在客户程序那里执行这个同样的语句好得多。我相信导致这个现象的一个原因是,在从数据库服务器到应用程序获取所需要的时间有过多的负载参与,并且其中的一个负载就是在ADO之间建立数据库的连接。Not sure whether you felt like this or not, when you are writing any stored proc Or a query, the query returns the results with better response time than the response time, when you execute that same query from your any client application. I believe, one of the reasons for such behavior is the overheads involved in getting the desired results from the database server to the client application; and one of such overheads is establishing the Connection between the ADO.
Web应用程序经常是在建立好与数据库的连接后,在完成相应的任务后马上就关闭此连接。同时注意到我们经常会写很多数据库驱动的应用程序。通常我们的应用程序都会有一个配置文件,会面里面指定一些相对固定的信息比如连接字符串在里面。这也意味着大多数时候我们都是连接到同一个数据库服务器,数据库,用相同的用户名和密码,以访问或大或小的数据。Web applications frequently establish the database connection and close them as soon as they are done. Also notice how most of us write the database driven client applications. Usually, we have a configuration file specific to our application and keep the static information like Connection String in it. That intern means that most of the time we want to connect to same database server, same database, and with same user name and password, for every small and big data.
ADO.NET 和IIS使用了一个叫连接池的接术,对于上面提到的这种设计,利用这种技术将分对应用程序十分的有帮助。这种技术工作方式是这样的:当第一请求到来时,他如实的建立起应用程序和数据库的连接,当连接建立起来并且应用程序完成具体的任务后请求关闭连接时,ADO.NET并没有消毁这个连接而是创建一个连接池,并且将这个翻译的连接对象放到连接池中同时保持与该连接对象的引用。等到下次请执行查询/存储过程时,就避免了很重的创建连接的过程而只是从数据库中抓取相应的连接再执行相应的查询/存储过程。通过这种方式相应的提高了查询的速度。ADO.NET with IIS uses a technique called Connection Pooling, which is very helpful in applications with such designs. What it does is, on first request to database, it serves the database call. Once it is done and when client application requests for closing the connection, ADO.NET does not destroy the complete connection, rather it creates a connection pool and puts the released connection object in the pool and holds the reference to it. And next time when the request to execute any query/stored proc comes up, it bypasses the hefty process of establishing the connection and just picks up the connection from the connection pool and uses that for this database call. This way, it can return the results faster comparatively.
让我们看一下连接池创建的更多细节。
Let us see Connection Pooling Creation Mechanism in more detail.
连接池和连接字符串是关联在一起的。每一个连接池和精确的连接字符串相对应,指定到应用程序。换句话说:一个独立的线程池是与每一个进程,应用程序域和连接字符串关联在一起的。Connection Pool and Connection String goes hands in hands. Every connection pool is associated with distinct connection string and that too, it is specific to the application. What in turn means is – a separate connection pool is maintained for every distinct process, app domain and connection string.
当所有的数据库请求都是通过ADO.NEt来发出的,ADO.NET在池中利用连接字符串精确匹配在同一应用程序域和进程中查找。如果在池中没有找中,ADO.NET将创建一个新的连接,如果找到了,它将试着从连接池中进行数据库连接。如果没有可用的空闲的连接,将创建一个新的连接,并且把它放到连接池中。按着这种方式,新创建的连接将一直添加到池中直到达到最大连接池数,这时如果ADO.NET接到新的连接请求,它将等待直到超时,这时将抛出错误。When any database request is made through ADO.NET, ADO.NET searches for the pool associated with exact match for the connection string, in the same app domain and process. If such pool is not found, ADO.NET creates a new one for it, however, if it is found, it tries to fetch the usable connection from that pool. If no usable free connection is found in the pool, new connection is created and added to the pool. This way, new connections keeps on adding to the pool till Max Pool Size is reached, after that when ADO.NET gets request for further connection, it waits for Connection Timeout time and then errors out.
现在又出现了一个新的问题,这些连接是在什么时候从连接池中释放出来的?当一个连接服务完后,被close/dispose,这个连接将被放入连接池,并标识为可被使用,相应的,如果一个连接未被显式的close/dispose,它将不会立即放入到连接池中。我们可以通过显示的调用连接对象的close()或Dispose()方法或者使用C#的using语句块来实例化对象。强烈建议当我们使用完连接对象后调用Close()或Dispose()来显示的关闭连接,而不是等到垃圾回收或Connection Pooler来帮你做。 Now the next question arises is - How any connection is released to pool to be available for such occasions? Once any connection has served and is closed/disposed, the connection goes to the connection pool and becomes usable. At times, connections are not closed/disposed explicitly, these connections do not go to the pool immediately. We can explicitly close the connection by using Close() or Dispose() method of connection object Or by using the "using" statement in C# to instantiate the connection object. It is highly recommended that we close or dispose(don't wait for GC or connection pooler to do it for you) the connection once it has served the purpose.
当一个应用程序域被关闭时,与其关联的连接池将马上被移除掉。举个例子来说,如果你有一个ASP.NET应用程序,连接池在你第一次接触数据库时被创建起来,同时在我们运行iisreset的时候,这些连接池也将被清除掉。在后面的我们会在一个例子中看到这个过程。需要注意到的是连接池是与IIS Web Server相关联起来的,而不是与你的开发环境,所以你不要期望在关闭 Visual Studio.Net时会关闭你的连接池。Connection Pool is removed as soon as the associated app domain is unloaded. Once the app domain is unloaded, all the connections from the connection pool becomes invalid and are thus removed. Say for example, if you have an ASP.NET application, the connection pool gets created as soon as you hit the database very first time, and connection pool is destroyed as soon as we do iisreset. We'll see it later with example. Note that connection pooling has to do with IIS Web Server and not with the Dev Environment, so do not expect the connection pool to be cleared automatically by closing your visual studio .Net dev environment.
ADO.NET 2.0引入了两个新的方法以清除连接池:ClearAllPools和ClearPool.ClearAllPools 清除指定提供者的连接池,ClearPool 清除指定连接的连接池。当在调用它们的时候如果有正在使用的连接,那么这些连接会被做一个适当的标记,当它们被关闭的时候,它们将被丢弃而不是放入连接池中。ADO.NET 2.0 introduces two new methods to clear the pool: ClearAllPools and ClearPool. ClearAllPools clears the connection pools for a given provider, and ClearPool clears the connection pool that is associated with a specific connection. If there are connections in use at the time of the call, they are marked appropriately. When they are closed, they are discarded instead of being returned to the pool.
请参考小节"查看ADO.NET连接池的简单方法"以获得如何查看连接池状态的方法。Refer to the section "Simple ways to View Connections in the pool created by ADO.NET" for details of how to determine the status of the pool.
连接字符串在连接池中扮演着非常重要的角色。ADO.NET和数据库服务器的连接仅以连接字符串为基础。下表是用于连接池中比较重要的关键字和其描述。Connection string plays a vital role in connection pooling. The handshake between ADO.NET and database server happens on the basis of this connection string only. Below is the table with important Connection Pooling specific keywords of the connection strings with their description.
Name |
Default |
Description |
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. A value of zero (0) causes pooled connections to have the maximum connection timeout. |
Connection Timeout |
15 |
Maximum Time (in Secs) to wait for a free connection from the tool |
Enlist |
'true' |
When true, the pooler automatically enlists the connection in the creation thread's current transaction context. Recognized values are true, false, yes, and no. Set Enlist = "false" to ensure that connection is not context specific. |
Max Pool Size |
100 |
The maximum number of connections allowed in the pool. |
Min Pool Size |
0 |
The minimum number of connections allowed in the pool. |
Pooling |
'true' |
When true, the SQLConnection object is drawn from the appropriate pool, or if it is required, is created and added to the appropriate pool. Recognized values are true, false, yes, and no. |
Incr Pool Size |
5 |
Controls the number of connections that are established when all the connections are used. |
Decr Pool Size |
1 |
Controls the number of connections that are closed when an excessive amount of established connections are unused. |
* Some Table contents are extracted from Microsoft MSDN Library for reference
除了以上所提到的关键字,还有一个比较重要的事情需要注意:如果你使用集成安全验证,连接池将为每个访问系统的用户创建相应的连接池。不过,你要是使用了user id和对应的密码来编写连接字符串,这时只会维持一个连接池。在后面这种情况,每个用户都可以使用或释放其它用户所创建的连接池。因此使用user id和password将具有更好的性能体验。Other than the above mentioned keywords, one important thing to note here. If you are using Integrated Security, then the connection pool is created for each user accessing the client system, whereas, when you use user id and password in the connection string, single connection pool is maintained across for the application. In the later case, each user can use the connections of the pool created and then released to the pool by other users. Thus using user id and password is recommended for better end user performance experience.
带连接池相关的关键字的连接字符串,会像下面这个样子。The connection string with the Pooling related keywords would look somewhat like this
initial catalog=Northwind; Data Source=localhost; Connection Timeout=30; User Id=MYUSER; Password=PASSWORD; Min Pool Size=20; Max Pool Size=200; Incr Pool Size=10; Decr Pool Size=5;
在关闭户端程序时,我们可以查看池中的连接。这是数据库相关的东西,因此如果需要查看活动的连接,我们就要利用到数据库相关的查询工具。We can keep a watch on the connections in the pool by determining the active connections in the database after closing the client application. This is a database specific stuff, so to see the active connections in the database server we must have to use database specific queries. This is with the exception that connection pool is perfectly valid and none of the connection in the pool is corrupted.
对于MS SQL Server:打开查询分析器,执行:exec sp_who
For MS SQL Server: Open the Query Analyser and execute the query : EXEC SP_WHO
对于Oracle:打开SQL Plus或者其他类似PL/SQL的开发环境或者TOAD,执行以下查询:
SELECT * FROM V$SESSION WHERE PROGRAM IS NOT NULL
For Oracle : Open the SQL Plus or any other editor like PL/SQL Developer or TOAD and execute the following query -- SELECT * FROM V$SESSION WHERE PROGRAM IS NOT NULL
我们以SQL Server2000为示例
1. 创建一个简单的ASP.NET Web应用程序。
2. 打开一个新的查询分析器,运行 exec sp_who.注意loginname 列,然后找到MACHINENAME\ASPNET.如果你没有运行任何ASP.NET应用程序,你将找不到该行。
3.在默认启动的页的Page Load事件里,写一个使用到数据库的方法。可以使用连接字符串像这个样子:"initial catalog=Northwind; Min Pool Size=20;Max Pool Size=500; data source=localhost; Connection Timeout=30; Integrated security=sspi"
4. 运行你的ASP.NET应用程序。
5. 然后重复步骤2,观察运行结果,你可以发现,那里有20(Min Pool Size)个连接。需要注意的是你只使用数据库一次。
6. 关闭你的Web页面,重复步骤2,你可以观察到即使你关闭了web页面,这些连接依然存在。
7. 现在重启IIS,你可以在运行...里执行:iisreset.
8. 现在步骤2,你将发现那20个连接已经不存在了。这是因为你的应用程序在IIS重启 时已经被 卸载了。
:
All right, let us do it with SQL Server 2000
1. Create a Sample ASP.NET Web Application
2. Open an instance of Query Analyzer and run the EXEC SP_WHO query. Note the loginname column, and look for MACHINENAME\ASPNET. If you have not run any other ASP.NET application, you will get no rows with loginname as "MACHINENAME\ASPNET".
3. On Page load of default startup page, add a method that makes a database call. Say your connection string is "initial catalog=Northwind; Min Pool Size=20;Max Pool Size=500; data source=localhost; Connection Timeout=30; Integrated security=sspi"
4. Run your ASP.NET application
5. Now repeat Step 2 and observe that there are exactly 20 (Min Pool Size) connections in the results. Note that you made the database call only once.
6. Close the web page of your web application and repeat step 2. Observe that even after you closed the instance of the web page connections persists.
7. Now Reset the IIS. You can do that by execute the command "iisreset" on the Run Command.
8. Now Repeat Step 2 and observe that all the 20 connections are gone. This is because your app domain has got unloaded with IIS reset.
1. 你接到如下的异常信息:"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached"
You receive the exception with the message: "Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached" in your .NET client application.
这个是因为发生在你使用发超过Max Pool Size的连接。默认的max pool size是100.如果我们需要使用超过max pool size的连接,ADO.NET将等待可用的连接,直到超时,则会出现以上错误信息。This occurs when you try using more than Max Pool Size connections. By default, the max pool size is 100. If we try to obtain connection more than max pool size, then ADO.NET waits for Connection Timeout for the connection from the pool. If even after that connection is not available, we get the above exception.
解决办法: Solution(s):
1. 最重要的步骤是:我们要确保每个连接都是显式的被打开,再被关闭。经常发生的情况是:我们打开了一个连接,执行了所需要的数据库操作,但是我们没有显示的关闭连接。正常来讲这个连接是不能再被从连接池中使用的。应用程序只有等到垃圾回收来释放它,在这这前这个连接在池中都是被标为不可用的。在这种情况下,既使你没有同时使用到最大连接池数指定的连接,你也会遇到该错误 。这也是这个问题最大的可能原因。Very first step that we should do is – Ensure that every connection that is opened, is Closed explicitly. At times what happens is, we open the connection, performs the desired database operation, but we do not close the connection explicitly. Internally it cannot be used as available valid connection from pool. The application would have to wait for GC to claim it, until then it is not marked as available from pool. In such case, even though you are not using max pool size number of connection simultaneously, you may get this error. This is the most probable cause of this issue.
2. 增加Max Pool Size到一个充分的值,你可以在连接字符串中包含“Max Pool Size = N”做到这点,N是新的Max Pool Size.Increase Max Pool Size value to a sufficient Max value. You can do so by including "Max Pool Size = N;" in the connection string, where N is the new Max Pool size.
3. 关闭连接池功能。虽然不是个好主意,毕竟它会影响性能,但是比起错误还是好得多。Set the Pooling Off. Well, this indeed is not a good idea as Connection Pooling puts a positive performance effect but it definitely is better that getting any such exceptions.
2. 遇到错误信息:"A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - Shared Memory Provider: )"。
You receive the exception with the message: "A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - Shared Memory Provider: )" in your ASP.NET application with MS SQL Server
这个情况是因为SQL Server遇到了一些Issues而不得不去刷新所有的连接,但是ADO.net仍然认为这些连接是有效的。This occurs when MS SQL Server 2000 encounter some issues and has to refresh all the connections and ADO.NET still expects the connection from the pool. Basically, it occurs when connection pool gets corrupted. What in turn happens is, ADO.NET thinks that the valid connection exists with database server, but actually, due to database server getting restarted it has lost all the connections.
Solution(s) : 解决办法
1. 如果你使用Oracle ODP.NET V9.2.0.4以上,你可能在连接字符串中加上"Validate Connection=true","validcon=true"可能在ODP.NET V9.2.0.4以前工作。
If you are working with .NET and Oracle using ODP.NET v 9.2.0.4 or above, you can probably try adding "Validate Connection=true" in the connection string. Well, in couple of places, I noticed people saying use "validcon=true" works for them for prior versions on ODP.NET. See which works for you. With ODP.NET v 9.2.0.4, "validcon=true" errors out and "Validate Connection=true" works just fine.
2. 如果你使用.NET 2.0 和SQL Server,你可以通过调用连接对象的ClearPoool方法来清除这些连接。SQLClient和OracleClient 都实现了这些方法。If you are working with .NET 2.0 and MS SQL Server, You can clear a specific connection pool by using the static (shared in Visual Basic .NET) method SqlConnection.ClearPool or clear all of the connection pools in an appdomain by using the SqlConnection.ClearPools method. Both SqlClient and OracleClient implement this functionality.
3. If you are working with .NET 1.1 and MS SQL Server,
a. In the connection string at the run time append a blank space and try establishing the connection again. What in turn it would do is, a new connection pool would be created and will be used by your application, In the meantime the prior pool will get removed if it's not getting used.
b. Do exception handling, and as soon as you get this error try connection afresh repeatedly in the loop. With time, ADO.NET and database server will automatically get in sync.
Well, I am not totally convinced with either approach, but frankly speaking, I could not get any better workable solution for this so far.
3. Leaking Connections
When we do not close/dispose the connection, GC collects them in its own time, such connections are considered as leaked from pooling point of view. There is a strange possibility that we reach max pool size value and at that given moment of time without actually using all of them, having couple of them leaked and waiting for GC to work upon them. This would actually lead to the exception mentioned above, even if we are not using max pool size number of connections.
Solution(s):
1. Ensure that we Close/Dispose the connections once its usage is over.
1. ADO.NET Connection Pooling is enabled/true by default, so it is advised to configure all connection string properly.
2. If you are using integrated security in web application, then connection pool is created for each user, whereas, when you use the user id and password single connection pool is maintained and used for the application.
3. If database has to restart or encounter any issues and removes all connections of the pool, it may not send any notification to ADO.NET(IIS) informing the same. It is advised that when we attempt to use the connection from pool, we do the appropriate exception handling. For more information, refer to the point 2 of Common Issues/Exceptions/Errors section.
4. Monitor the CPU usage of your database server while choosing the values for pooling related options of connection string.
1. http://samples.gotdotnet.com/QuickStart/howto/doc/adoplus/connectionpooling.aspx
2. ADO.NET Connection Pooling Explained :::: http://www.ondotnet.com/pub/a/dotnet/2004/02/09/connpool.html
3. The .NET Connection Pool Lifeguard :::: http://msdn2.microsoft.com/en-us/library/aa175863(SQL.80).aspx
In nutshell, Connection pooling can increase the performance of any application by using active connections of the pool for consecutive requests, rather than creating a new connection each time. By default, ADO.NET enables and uses Connection Pooling due to its positive impact. And at the same time, the developer who is the best judge of his/her application, can configure the connection pooling features, or can even switch it off, based on the applications need by simply using power keywords of connection string.
Please spare some time to rate and provide feedback about this article. Your couple of minutes can help in enhancing the quality of this article.