为啥 Oledb Connection.Close() 执行时间太长?

Posted

技术标签:

【中文标题】为啥 Oledb Connection.Close() 执行时间太长?【英文标题】:Why does Oledb Connection.Close() take far too long to execute?为什么 Oledb Connection.Close() 执行时间太长? 【发布时间】:2019-04-26 15:05:09 【问题描述】:

在开发连接到本地数据库的桌面应用程序期间,我将数据库移动到网络位置,现在每次调用 Connection.Close() 程序都会挂起 5-15 秒。当数据库存储在本地计算机上时,我很少会看到这个问题,但是现在它在网络上,几乎每次我尝试关闭()时它都会挂起。我对数据库的第一次调用我什至没有查询它,它只是一个测试连接,我打开和关闭以确保用户可以连接,但它仍然挂起太久。

我之前看到过这个问题,但除了“尝试使用 () 让 c# 清理它”之外,没有人可以提供修复的建议或解决方案。这不会以任何方式影响 Close() 时间。

连接字符串中是否有解决此问题的选项?有谁知道为什么会这样?

我使用的连接字符串是:

CONNECTION_STRING = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=\\NEWTORK\Shared\Database\Database.accdb; Persist Security Info=False;"

private void

  Form_Login_Load(object sender, EventArgs e)
  
    OleDbConnection Connection = new OleDbConnection();
    Connection.ConnectionString = CONNECTION_STRING;
    try
    
      Console.Write("Connection Opening.....");
      Connection.Open();
      Console.WriteLine("Connection Opened");
      Console.Write("Writing Status Text.....");
      lbl_Status.Text = "Online";
      Console.WriteLine("Written Status Text");
      Console.Write("Connection Closing.....");
      Connection.Close();
      Console.WriteLine("Connection Closed");
    
    catch (Exception Ex)
    
      lbl_Status.Text = "Offline";
      lbl_Status.ForeColor = System.Drawing.Color.FromArgb(255, 0, 0);
      MessageBox.Show("Could not connect to Database");
    
  

在我的输出窗口中,我立即看到打开消息和写入状态消息,但应用程序在 'Console.Write("Connection Closing.....");' 之前挂起线。 5-15 秒后,关闭消息出现在窗口中。

应用程序中有许多查询数据库的连接,并且在尝试关闭所有连接之前似乎挂起。我似乎确实注意到在不关闭应用程序的情况下重复相同的查询有时会导致重复关闭的关闭时间更快,但它总是在第一次尝试任何查询时挂起。

【问题讨论】:

将代码放入 BackGroundWorker,这样表单就不会挂起。我已经这样做了很多次了。 @jdweng 我以前从未使用过 BackGroundWorker,所以很可能我做错了什么。我在这段代码中添加了一个,虽然它确实允许在关闭原始连接之前显示表单,但它也允许我尝试登录到应用程序,该应用程序也打开了与数据库的连接并抛出异常,因为它正在尝试在前一个连接关闭之前打开一个新连接。 @G。 Maniatis 你需要等到 BackgroundWorker 完成,通过使用.RunWorkerCompleted 事件。 【参考方案1】:

最终对我有用的是: 当我的 Connection.Close() 方法的延迟时间很长时,我使用的是 Microsoft Access 数据库引擎 2016 (x64)。出于不相关的原因,我需要卸载 2016 引擎并使用 2010 (x86) 版本。现在我的 Connection.Close() 时间平均约为 40 毫秒,这对于我的应用程序来说是完全可以接受的。

【讨论】:

【参考方案2】:

如果数据库和程序之间有任何待处理的事务,那么Close() 将回滚它们。它还必须从连接池中请求并删除它,这在远程驱动器上可能需要更长的时间。这可能是你的问题吗?

Here are the docs on the method.

要解决这个问题,您可以使用 BackgroundWorker 来执行它,如下所示:

var b = new BackgroundWorker();
b.DoWork += CloseDB;
b.RunWorkerCompleted += someMethodAfterClose;
b.RunWorkerAsync();

关闭数据库:

public void CloseDB(object sender, DoWorkEventArgs e) 
    someConnection.Close();

【讨论】:

我不确定如何检查任何待处理的交易。这些文档在这方面不是很有帮助。我试图实现一个 BackGroundWorker,但没有成功。我可以在加载后立即显示表单,但是在后台工作人员等待它时连接仍然挂起,同时我可以执行另一个引发异常的数据库查询,因为连接尚未关闭。我可以像@jdweng 建议的那样尝试RunWorkerCompleted,但这并不能解决延迟问题,所以我回到了开始的地方。 您需要做的就是使用.RunWorkerCompleted 等待它完成。【参考方案3】:

这通常是服务器设置。如果防火墙 + windows 防御者处于活动状态,那么它会扫描所有“文件”访问 - 结果是打开速度很慢,当然还有 close()。

尝试运行共享文件夹所在的计算机,并关闭防火墙和 Windows Defender。我看到这经常解决大的延迟。当然,使用基于套接字的技术将消除这个问题(例如:基于服务器)。然而,你有你所拥有的,而且通常不是你的代码速度,而是“windows文件”系统,以及网络和防病毒软件的系统,这是导致速度变慢的原因。

【讨论】:

【参考方案4】:

这可能会被标记为“不是答案”,但我也有这个问题,似乎没有人找到真正的解释,所以与其创建一个多余的问题,这里是我的一些症状:

当办公室更新到 Windows 10(从 Windows 7)时开始更频繁地发生。 经常发生但并非总是如此。有时 OleDb 连接关闭/处理非常快,有时会挂起约 10-15 秒。任何时候任何给定的连接尝试的变化似乎都是随机的,即使使用相同的 Access 数据库、相同的机器和相同的进程。 本地和网络上的数据库都可能发生挂起,但尚未测试其中一个是否比另一个更频繁。 即使连接未进行任何更改或事务(即仅 SELECT 查询)也会发生。 仅适用于 OleDb 连接,即用户可以通过 Access 接口打开和关闭数据库。

更新

无奈之下,我尝试了在后台线程中关闭连接的建议,以避免在此过程中阻塞主线程。但是当我这样做时,每当稍后启动后续连接时,都会出现此错误:

未处理的异常:System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏

谷歌搜索表明,许多人在处理 Access 数据库连接时似乎都会发生这种情况,但通常是出于未知原因。

所以我尝试了一个不同的建议,即更改 conn 字符串以包含 OLE DB Services=-1;。起初这似乎解决了问题。没有这方面的专家,据我所知,即使在处理了 conn 对象之后,它也基本上避免了关闭时挂起的问题,方法是在幕后打开一些连接的资源以供重用。我很好......除了它最终似乎关闭了这些资源(可能是一些超时),然后当稍后建立连接时,回到上面莫名其妙的AccessViolationException

潜在解决方案

从拼凑各种网络 cmet 和我自己的实验:

OleDb 不能很好地处理多线程。 关闭应用程序中的最后个打开的 Access 连接,无论是什么数据库,似乎都会触发 OleDb 库本身内某些资源的卸载,因此这是一个潜在的耗时操作。

所以我做了什么,添加一个隐藏在我的应用程序中的空 Access 数据库,并在启动时打开一个 OleDb 连接。我不处理连接并维护对连接的引用以防止它自行关闭。这样,任何后续连接的处理都不会触发 OleDb 保留的任何资源的卸载。

这是一个 hack,但到目前为止,这似乎解决了最初的问题。

当然,真正的答案是避免访问数据库!

【讨论】:

以上是关于为啥 Oledb Connection.Close() 执行时间太长?的主要内容,如果未能解决你的问题,请参考以下文章

Connection: close

使用 Asp.net 连接字符串的 connection.close 和 connection.dispose 之间的区别? [复制]

为啥 System.Data.OleDb 代码会联系 Microsoft?

OleDb 数据源 - 为啥它会出现在这个随机位置? [复制]

即使在 connection.close() 调用之后,连接仍处于建立状态

转:Connection: close和Connection: keep-alive有什么区别?