数据库链接池,你不知道的秘密

x33g5p2x  于2022-03-07 转载在 其他  
字(3.4k)|赞(0)|评价(0)|浏览(190)

为什么需要链接池

众所周知,建立一个数据库连接是一件非常耗时(消耗时间)耗力(消耗资源)的事情。之所以会这样,是因为连接到数据库服务器需要经历几个漫长的过程:建立物理通道(例如套接字或命名管道)、与服务器进行初次握手、分析连接字符串信息、由服务器对连接进行身份验证、运行检查以便在当前事务中登记等等。我们先不管为什么会有这样的机制,存在总是有它的道理。既然新建一条连接如此痛苦,那么为什么不重复利用已有的连接呢? 实际上,ADO.NET 已经为我们提供了名为连接池的优化方法,当我们需要连接数据库服务器的时候,只需去池(容器)中取出一条空闲的连接,而不是新建一条连接。这样的话,我们就可以大大减少连接数据库的开销,从而提高了应用程序的性能。

什么是连接池

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

连接池工作原理

创建连接池

需要说明的是,连接池是具有类别区分的。也就是说,同一个时刻同一应用程序域可以有多个不同类型的连接池。那么,连接池是如何标识区分的?细致的讲,是由进程、应用程序域、连接字符串以及 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 不同,所以需要创建新的连接池。

分配空闲连接

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

  • 如果池中有空闲连接可用,返回该连接。
  • 如果池中连接都已用完,创建一个新连接添加到池中。
  • 如果池中连接已达到最大连接数,请求进入等待队列直到有空闲连接可用。

移除无效连接

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

回收使用完的连接

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

链接信息的重要属性

连接池的行为可以通过连接字符串来控制,也可以通过代码控制,主要包括四个重要的属性:Connection Timeout、、Max Pool Size、Min Pool Size、Pooling。

Enlist=true;Pooling=true;Max Pool Size=300;Min Pool Size=0;Connection Lifetime=300;user id=sa;packet size=1000 
packet size
SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
connStr.DataSource = @".\SQLEXPRESS";
connStr.InitialCatalog = "master";
connStr.IntegratedSecurity = true;
connStr.Pooling = true; //开启连接池
connStr.MinPoolSize = 0; //设置最小连接数为0
connStr.MaxPoolSize = 50; //设置最大连接数为50             
connStr.ConnectTimeout = 10; //设置超时时间为10秒
using( SqlConnection conn = new SqlConnection(connStr.ConnectionString))
{
    //todo
}

Connection Timeout:连接请求等待超时时间。默认为 15 秒,单位为秒。
Max Pool Size: 连接池中最大连接数。默认为 100。
Min Pool Size: 连接池中最小连接数。默认为 0。
Pooling: 是否启用连接池。ADO.NET 默认是启用连接池的,因此,你需要手动设置 Pooling=false 来禁用连接池。

连接池异常与处理方法

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

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;//设置最大连接池为5
            connStr.ConnectTimeout = 1;//设置超时时间为1秒

              SqlConnection conn = null;
            for (int i = 1; i <= 100; ++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 条连接时,由于连接池中连接数量已经达到了最大数并且没有空闲的连接,因此需要等待连接直到超时。当超过超时时间时,就出现了上述的连接异常。因此,我必须再次强调,使用完的连接应当尽快的正确的关闭和释放。

相关文章