C# 连接池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiangqiang2015/article/details/81902172

目录

1、数据库驱动

2、 ADO.NET、ADO、OLEDB、ODBC

3、 连接池

4、 池的创建和分配

5、 移除连接

6、 以Oracle为例,进行代码演示

6.1 池的创建

6.2 创建连接

6.3 将连接返还给池

6.4 释放连接池

6.5 演示:创建多余2个连接会是什么情况?

6.6 演示:关闭一个连接后,等待的连接会自动连接

6.7 演示:等待超时后并不会自动连接

6.8 演示:同一个进程中,连接字符串一样的连接共享连接池

6.9 演示:非同一进程,连接字符串一样的连接不共享连接池

7、总结


1、数据库驱动

数据库驱动是不同数据库开发商(比如oracle mysql等)为了某一种开发语言环境(比如C#、java等)能够实现统一的数据库调用而开发的一个程序接口。

以Oracle为例:安装了.Net FrameWork 后如果不安装Oracle的客户端,应用程序是无法连接Oracle数据库的,因为.Net的数据库访问API连接oracle时需要调用Oracle的客户端数据库访问驱动,例如我们经常出现的Oracle.DaraAccess.dll之类。

2、 ADO.NET、ADO、OLEDB、ODBC

发展史:ODBC->OLEDB->ADO->ADO.Net

ODBC:最早的通用数据访问接口,但是只支持访问关系型数据库。

OLEDB:通向不同的数据源的低级应用程序接口,OLE DB不仅包括微软资助的标准数据接口开放数据库连通性(ODBC)的结构化查询语言(SQL)能力,还具有面向其他非SQL数据类型的通路。

ADO:是OLEDB的简化版本,实在OLEDB接口之上附加的一层,简化了OLEDB的调用。

ADO.NET:ADO.NET在ADO的基础上进行了扩展和变化,随着支持的数据库类型越来越多、应用的语言特性越来越多譬如Linq to Dataset、实体对象模型的框架等变化太多就被认为是一个新的产品。ADO.Net 是一组向 .NET Framework 程序员公开数据访问服务的类。ADO.NET 提供对诸如 SQL Server 和 XML 这样的数据源以及通过 OLE DB 和 ODBC 公开的数据源的一致访问。

我的总结:可以认为这一票都是微软在数据访问功能方面设计和封装的一系列编程接口(API)。ODBC是微软关系数据库访问标准,以此标准封装了ODBC、OLEDB这些微软偏底层的接口,调用和操作比较复杂,ADO、ADO.NET是对ODBC、OLEDB的封装和扩展,是高级接口,便于程序员调用。

3、 连接池

连接到数据源可能需要很长时间。 打开连接的成本降到最低,ADO.NET 使用称为的优化方法连接池,其中重复打开和关闭连接的成本降至最低。 .NET Framework 数据提供程序处理连接池的方式有所不同。

连接池使新连接必须打开的次数得以减少。 池进程保持物理连接的所有权。 通过为每个给定的连接配置保留一组活动连接来管理连接。 每当用户在连接上调用 Open 时,池进程就会查找池中可用的连接。 如果某个池连接可用,会将该连接返回给调用者,而不是打开新连接。 应用程序在该连接上调用 Close 时,池进程会将连接返回到活动连接池集中,而不是关闭连接。 连接返回到池中之后,即可在下一个 Open 调用中重复使用。

4、 池的创建和分配

在初次打开连接时,将根据完全匹配算法创建连接池,该算法将池与连接中的连接字符串关联。 每个连接池都与一个不同的连接字符串相关联。 打开新连接时,如果连接字符串并非与现有池完全匹配,将创建一个新池。 按进程、应用程序域、连接字符串以及 Windows 标识(在使用集成的安全性时)来建立池连接。 连接字符串还必须是完全匹配的;按不同顺序为同一连接提供的关键字将分到单独的池中。

5、 移除连接

如果空闲时间达到大约 4-8 分钟,或池进程检测到与服务器的连接已断开,连接池进程会将该连接从池中移除。 注意,只有在尝试与服务器进行通信之后才能检测到断开的连接。 如果发现某连接不再连接到服务器,则会将其标记为无效。 无效连接只有在关闭或重新建立后,才会从连接池中移除。

如果存在一个与已消失的服务器的连接,即使连接池进程尚未检测到断开的连接,也可以从池中取出此连接并将连接标记为无效。 这种情况是因为检查连接是否仍有效的系统开销将造成与服务器的另一次往返,从而抵消了池进程的优势。 发生此情况时,初次尝试使用该连接将检测连接是否曾断开,并引发异常。

6、 以Oracle为例,进行代码演示

6.1 池的创建

初次创建连接时,.Net会自动依据连接创建对应的连接池。

6.2 创建连接

将池的最大和最小连接数都设置为2。

  try
            {
                m_OrclConnection = new OracleConnection();
                string sConenctStr = "Persist Security Info = True; Data Source = {0};"
                                    + "User ID = {1}; Password ={2};"
                                    + "Pooling=true;Max Pool Size=2;Min Pool Size=2;";
                sConenctStr = string.Format(sConenctStr, "ORCL_xxxxxxx", "XXXX", "XXXX");
                m_OrclConnection.ConnectionString = sConenctStr;
                Action actionConectStart = () =>
                  {
                      mmeEdit.Text += Environment.NewLine + DateTime.Now.Minute + "分" + DateTime.Now.Second + "秒";
                      mmeEdit.Text += Environment.NewLine + string.Format("开始打开数据库连接...", m_ListConn.Count);
                  };

                Action actionConnecting = () =>
                {
                    mmeEdit.Text += Environment.NewLine + string.Format("正在等待打开数据库连接...");
                };

                Action<string> actionConectDone = (time) =>
                {
                    mmeEdit.Text += Environment.NewLine + DateTime.Now.Minute + "分" + DateTime.Now.Second + "秒";
                    mmeEdit.Text += Environment.NewLine + string.Format("已经打开1个连接,耗时{0}毫秒...", time);
                    mmeEdit.Text += Environment.NewLine;
                };

                //// 为了不阻塞UI线程,异步执行
                Task.Run(() =>
                {
                    mmeEdit.Invoke(actionConectStart);
                    DateTime dateBegin = DateTime.Now;
                    m_OrclConnection.OpenAsync();       // 如果超过连接池最大数,此处会阻塞
                    var consumeTime = DateTime.Now - dateBegin;
                    if (m_OrclConnection.State == ConnectionState.Open)
                    {
                        mmeEdit.Invoke(actionConectDone, new object[] { consumeTime.Milliseconds.ToString() });
                    }
                    else if (m_OrclConnection.State == ConnectionState.Connecting)
                    {
                        mmeEdit.Invoke(actionConnecting);
                    }
                });

                m_ListConn.Add(m_OrclConnection);
                mmeEdit.Text += Environment.NewLine + string.Format("共创建了{0}个连接...", m_ListConn.Count);
            }
            catch (Exception ex)
            {
                mmeEdit.Text += ex.Message;
            }

6.3 将连接返还给池

使用Close方法时,并不会关闭连接,而是将连接归还给连接池管理。

if (m_ListConn.Count > 0)
{
                    var openConn = m_ListConn.First((item) => item.State ==ConnectionState.Open);                        
                    if (openConn == null)
                    {
                        mmeEdit.Text += Environment.NewLine + "无打开的连接...";
                    }
                    else
                    {
                        mmeEdit.Text += Environment.NewLine;
                        openConn.Close();
                        if (openConn.State == ConnectionState.Open)
                            mmeEdit.Text += Environment.NewLine + "关闭连接失败...";
                        else
                            mmeEdit.Text += Environment.NewLine + "关闭连接成功...";
                    }
                }

6.4 释放连接池

 if (m_OrclConnection != null)
            {
                //// 释放当前连接的连接池
                OracleConnection.ClearPool(m_OrclConnection);
                mmeEdit.Text += Environment.NewLine + "清除当前池成功..." + Environment.NewLine;
                m_ListConn.Clear();
            }
 if (m_OrclConnection != null)
            {
                //// 释放进程的所有连接池
                OracleConnection.ClearAllPools();
                mmeEdit.Text += Environment.NewLine + "清除所有池成功..." + Environment.NewLine;
                m_ListConn.Clear();
            }

6.5 演示:创建多余2个连接会是什么情况?

6.6 演示:关闭一个连接后,等待的连接会自动连接

6.7 演示:等待超时后并不会自动连接

等待的时间是有限制的,超时后不会自动连接。

6.8 演示:同一个进程中,连接字符串一样的连接共享连接池

打开一个主窗体,在主窗体中打开一个子窗体,在主窗体中先打开两个连接,然后再在子窗体中打开连接会发现打不开连接,说明其共享一个连接池。

备注:子窗体的代码和主窗体一模一样。

 

6.9 演示:非同一进程,连接字符串一样的连接不共享连接池

打开两个应用程序,都打开两个连接。

7、总结

  • 连接池充当了对活动连接的维护作用,我们代码Close连接后并不会关闭连接而是归还给连接池以供其他逻辑使用,这样我们每次打开连接并不需要建立物理链路,连接登录数据库实例等长时间的连接操作,而是有效的利用了连接池中活动的连接。
  • 连接池的创建比较严格,根据应用程序进程、应用程序域、连接字符串等严格匹配创建,参数顺序不同也会创建不同的连接池。
  • 等待连接的连接是由时间限制的,等待超时需要重新Open。
  • 单线程的进程中,如果是对同一数据库,用同一用户登录,只需创建一个连接,使用之后Close掉,归还给连接池。

参考地址:

https://www.cnblogs.com/liuzhendong/archive/2012/01/29/2331189.html

https://docs.microsoft.com/zh-cn/dotnet/framework/data/adonet/

https://www.cnblogs.com/wangcq/p/3614276.html

https://docs.microsoft.com/zh-cn/dotnet/framework/data/adonet/ole-db-odbc-and-oracle-connection-pooling

猜你喜欢

转载自blog.csdn.net/xiangqiang2015/article/details/81902172