“打开连接”实际上是啥意思?
Posted
技术标签:
【中文标题】“打开连接”实际上是啥意思?【英文标题】:What does "opening a connection" actually mean?“打开连接”实际上是什么意思? 【发布时间】:2011-04-20 05:36:44 【问题描述】:我正试图向某人解释为什么数据库连接实现 IDisposable,但我意识到我真的不知道“打开连接”的真正含义。 所以我的问题是——c# 打开连接时实际上做了什么?
谢谢。
【问题讨论】:
你的意思是数据库连接还是tcp连接?你需要扩展。 【参考方案1】:实现连接实际上涉及两个类(实际上更多,但我正在简化)。
其中之一是您在代码中使用的IDbConnection
实现(SQLConnection
、NpgsqlConnection
、OracleConnection
等)。另一个是程序集内部的“真实”连接对象,对您的代码不可见。我们暂时称它为“RealConnection
”,尽管它的实际名称因不同的实现而异(例如,在我最熟悉实现的 Npgsql 中,该类称为NpgsqlConnector
)。
当您创建IDbConnection
时,它没有RealConnection
。任何对数据库做某事的尝试都会失败。当您Open()
它时,会发生以下情况:
-
如果启用了池化,并且池中有一个
RealConnection
,则将其加入队列并使其成为 IDbConnection
的 RealConnection
。
如果启用池化,并且存在的RealConnection
对象总数大于最大大小,则抛出异常。
否则创建一个新的RealConnection
。初始化它,这将涉及打开某种网络连接(例如 TCP/IP)或文件句柄(对于 Access 之类的东西),通过数据库的握手协议(因数据库类型而异)并授权连接。这将成为IDbConnection
的RealConnection
。
在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 关于网站是正确的,我忘记了,因为我自己没有处理过。以上是关于“打开连接”实际上是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章