“打开连接”实际上是啥意思?

Posted

技术标签:

【中文标题】“打开连接”实际上是啥意思?【英文标题】:What does "opening a connection" actually mean?“打开连接”实际上是什么意思? 【发布时间】:2011-04-20 05:36:44 【问题描述】:

我正试图向某人解释为什么数据库连接实现 IDisposable,但我意识到我真的不知道“打开连接”的真正含义。 所以我的问题是——c# 打开连接时实际上做了什么?

谢谢。

【问题讨论】:

你的意思是数据库连接还是tcp连接?你需要扩展。 【参考方案1】:

实现连接实际上涉及两个类(实际上更多,但我正在简化)。

其中之一是您在代码中使用的IDbConnection 实现(SQLConnectionNpgsqlConnectionOracleConnection 等)。另一个是程序集内部的“真实”连接对象,对您的代码不可见。我们暂时称它为“RealConnection”,尽管它的实际名称因不同的实现而异(例如,在我最熟悉实现的 Npgsql 中,该类称为NpgsqlConnector)。

当您创建IDbConnection 时,它没有RealConnection。任何对数据库做某事的尝试都会失败。当您Open() 它时,会发生以下情况:

    如果启用了池化,并且池中有一个 RealConnection,则将其加入队列并使其成为 IDbConnectionRealConnection。 如果启用池化,并且存在的RealConnection 对象总数大于最大大小,则抛出异常。 否则创建一个新的RealConnection。初始化它,这将涉及打开某种网络连接(例如 TCP/IP)或文件句柄(对于 Access 之类的东西),通过数据库的握手协议(因数据库类型而异)并授权连接。这将成为IDbConnectionRealConnection

IDbConnection 上执行的操作会变成RealConnection 在其网络连接(或其他)上执行的操作。将结果转化为实现IDataReader等的对象,从而为您的编程提供一致的接口。

如果IDataReader 是用CommandBehavior.CloseConnection 创建的,那么该数据读取器将获得RealConnection 的“所有权”。

当您致电Close() 时,会发生以下情况之一:

    如果池化,并且池未满,则将对象放入队列以供后续操作使用。 否则RealConnection 将执行任何协议定义的过程来结束连接(向数据库发出连接将要关闭的信号)并关闭网络连接等。然后对象可能会超出范围并可用于垃圾收集。

如果CommandBehavior.CloseConnection 发生了例外情况,在这种情况下是Close()Dispose()IDataReader 调用触发。

如果您致电Dispose(),则与Close() 一样会发生同样的事情。不同之处在于Dispose() 被视为“清理”并且可以与using 一起使用,而Close() 可能在生命周期的中间使用,然后是稍后的Open()

由于RealConnection 对象的使用以及它们被池化的事实,打开和关闭连接从相对较重的东西变为相对较轻的东西。因此,与其让连接长时间保持打开以避免打开它们的开销很重要,不如让它们保持打开尽可能短的时间变得很重要,因为RealConnection 会为您处理开销,并且您使用它们的速度越快,池连接在使用之间共享的效率就越高。

还请注意,您可以拨打Dispose()IDbConnection,而您已经拨打了Close()(这是一条规则,无论在什么状态下,拨打Dispose() 都应该是安全的,事实上,即使它已经被调用)。因此,如果您手动调用Close(),最好将连接放在using 块中,以捕获在调用Close() 之前发生异常的情况。唯一的例外是您实际上希望连接保持打开状态;假设您要返回使用CommandBehavior.CloseConnection 创建的IDataReader,在这种情况下,您不会处置IDbConnection,而是处置阅读器。

如果您未能释放连接,则RealConnection 将不会返回池以供重复使用,也不会执行其关闭程序。要么池将达到其限制,要么底层连接的数量将增加到损害性能并阻止更多创建的点。最终,RealConnection 上的终结器可能会被调用并导致此问题得到修复,但终结器只会减少损坏并且不能依赖。 (IDbConnection 不需要终结器,因为它是 RealConnection 持有非托管资源和/或需要关闭)。

除此之外,还可以合理地假设,对于 IDbConnection 的实施还有其他一些独特的处置要求,即使分析上述内容让您认为没有必要,它仍然应该被处置(例外是CommandBehavior.CloseConnection 将所有处置负担转嫁给IDataReader,但处置该读者同样重要)。

【讨论】:

优秀的答案,非常有见地。 +1 @RPM1984 谢谢。我为 Npgsql 贡献了一点点,包括 NpgsqlConnector 的工作原理,不久前并从中学到了很多。这是一个相对较小的程序集和开源,所以如果你认为你会发现更多关于这个有趣的东西,请看一下【参考方案2】:

添加到上面的答案...关键是,在“打开连接”时,可能会分配比标准垃圾收集更多的资源来恢复,即某种打开的套接字/管道/IPC。 Dispose() 方法清除这些。

【讨论】:

【参考方案3】:

好问题。

根据我对 SQL 连接“幕后”工作的了解(知识有限),涉及许多步骤,例如:

引擎盖下的步骤

    物理套接字/管道已打开(使用给定的驱动程序,例如 ODBC) 与 SQL Server 握手 协商的连接字符串/凭据 事务范围

更不用说连接池,我相信这涉及到某种算法(如果连接字符串与一个已经存在的池匹配,则将连接添加到池中,否则创建新的)

IDiposable

关于 SQL 连接,我们实现了 IDisposable,以便当我们调用 dispose(通过 using 指令或显式)时,它将连接放回连接池。这与普通的旧 sqlConnection.Close() 形成鲜明对比 - 因为所有这些只是暂时关闭它,但保留该连接以供以后使用。

据我了解,.Close() 关闭与数据库的连接,而 .Dispose() 调用 .Close(),然后 然后 释放非托管资源。

牢记这些要点,至少实现 IDisposable 是一种好习惯。

【讨论】:

不,Dispose 的作用与 Close 相同。将在答案中详细说明。 @Jon Hanna - 没有(AFAIK),我会找到一篇文章来证明这一点。 据我了解,close只是关闭sql连接,dispose调用close并释放非托管资源。不过我可能是错的。 不用怀疑,拆开Reflector看看。对于 SqlConnection,Close 和 Dispose 之间的唯一区别是 Dispose 还导致 SqlConnection 对象从其 Component 站点中删除(SqlConnection 派生自 Component)。当然,这只有在将 SqlConnection 对象添加到站点时才有意义(例如,将其拖放到表单上)。 如果正在使用池,则既不处置也不关闭关闭 sql 连接,已在我的回答中进行了描述。 Tergiver 关于网站是正确的,我忘记了,因为我自己没有处理过。

以上是关于“打开连接”实际上是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

Netstat -ab 中的“无法获取所有权信息”是啥意思?

“站点包”中的“站点”实际上是啥意思?

内存重用实际是啥意思?

RepeatedKFold 实际上是啥意思?

FFT的大小实际上是啥意思

Heredoc:常用的“EOT”实际上是啥意思?