【问题标题】:How to cancel a long-running Database operation?如何取消长时间运行的数据库操作?
【发布时间】:2009-05-20 17:00:35
【问题描述】:

目前正在与 Oracle 合作,但也需要 MS SQL 的解决方案。

我有一个 GUI,允许用户生成将在数据库上执行的 SQL。这可能需要很长时间,具体取决于它们生成的搜索。我希望 GUI/App 在此搜索期间做出响应,并且我希望用户能够取消搜索。

我正在使用后台工作线程。

我的问题是,当用户取消搜索时,我无法中断对数据库的调用。它一直等到完成,然后它可以轮询“CancelationPending”属性。这不仅会浪费数据库资源,还会给我的代码带来问题。

如果用户在很长的查询中点击“搜索”,然后点击“取消”,然后再次点击“搜索”——第一次搜索仍在数据库中进行。当他们再次点击搜索时,后台工作人员仍然很忙。我对这个问题的唯一解决方案是创建一个新的后台工作人员。

这似乎是一种非常丑陋的做事方式。数据库继续工作我正在创建后台工作人员的新实例....当我真的想停止数据库调用并重新使用同一个工作人员时。

我该怎么做?

【问题讨论】:

  • 感谢所有帖子;我真的不确定哪个答案是最好的;所有的赞成票似乎都来自我。如果我做出了错误的选择,我深表歉意。

标签: .net database oracle asynchronous backgroundworker


【解决方案1】:

如果您使用 ADO.NET 和 SQL 数据提供程序,请查看 SqlCommand.Cancel 方法。这就是你要找的。但是,它会尝试取消并且取消可能需要一些时间。基本上,由 SQL Server 决定何时批准您的取消请求。当查询被取消时,你应该得到一个 SqlException 指示操作被用户取消。显然,您不想将此异常视为异常并对其进行特殊处理,例如如果SqlException是由于用户取消操作而导致的,则将其吞下。

【讨论】:

    【解决方案2】:

    我还注意到 command.Cancel() 并没有真正中止命令。对我有用的是在用户中止时关闭连接(如果您使用一个则回滚事务)。这将在命令执行时在后台线程中引发异常,因此您必须捕获它并检查那里的 CancellationPending 属性,并且在这种情况下不要重新引发异常...

    // When aborting
    worker.CancelAsync();
    command.Connection.Close();
    
    // In your DoWork event handler
    ...
    catch (Exception)
    {
        if (worker.CancellationPending)
        {
            e.Cancel = true;
            return;
        }
        else
        {
            throw;
        }
    }
    
    // And in your RunWorkerCompleted event handler
    if (e.Error == null && !e.Cancelled)
    {
        ...
    }
    

    【讨论】:

    • 我正在尝试采用这个建议,但得到一个我无法捕捉到的本地异常,这阻止了我这样做。你有使用 SQLCE 的这种方法的经验吗?
    • 您需要调用 SqlConnection.ClearPool 以确保连接不会回到池中...objectmix.com/ado-dao-rdo-rds/…stackoverflow.com/questions/1145892/…
    • @MatthewBelk:您应该能够在 catch 子句中不使用过滤器来捕获此类异常,即 catch { // 您的代码 }。
    【解决方案3】:

    我很确定这是可能 - 我们使用 TOAD for Oracle,它可以让您取消长时间运行的查询,as described here。不过我不确定他们是怎么做到的。

    【讨论】:

      【解决方案4】:

      您可以让后台工作人员在不同的线程上触发实际的数据库调用,然后定期检查数据库调用是否已完成或是否已按下取消,此时您可以终止数据库线程.这实际上不会帮助数据库加载任何内容(因为您的查询已经发送并且仍在处理中),但它确实释放了与它相关的本地资源。

      【讨论】:

        【解决方案5】:

        我认为最好的解决方案似乎是通过监控表终止会话。

        使用 Oracle,您可以像 Burnsys 所说的那样做到这一点

        在 Firebird 2.5 中,它看起来像 same

        我希望 SQL 女士中存在类似的东西

        【讨论】:

          【解决方案6】:

          如果您使用的是 SQLCommand,您可以尝试调用它的 Cancel 方法。

          【讨论】:

            【解决方案7】:

            如何打开与数据库的新连接,以 sysdba 身份登录并发送“ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE”命令,指定要终止的进程的 SID。

            要获取 sessionID:select sid from v$mystat where rownum = 1

            要获取序列号:select sid, serial# from v$session where sid = :SID

            http://www.oracle-base.com/articles/misc/KillingOracleSessions.php

            编辑:此处不以 sysdba 身份登录的 WW 想法:http://forums.oracle.com/forums/thread.jspa?threadID=620578

            【讨论】:

            • 从安全角度来看,让您的应用程序以 sysdba 身份登录并不是一个好主意。我建议将此 kill 命令包装在只能杀死某些会话的 PL/SQL 包中。
            • 没错,无论如何,你可能需要一些特殊的权限来运行SQL包,而且你必须建立一个新的连接来发送kill命令,所以你会用特殊的登录到基地权限仅用于终止会话,应用程序的其余部分可以使用与之前相同的用户。
            【解决方案8】:

            我已尝试使用 ADO 2.8 和 SQLOLEDB 或 SQL Server 本机客户端取消和关闭。使用取消,记录集停止获取数据,但在后台继续从服务器读取并消耗应用程序的内存。在 32 位应用程序中,您可能会在几分钟后收到“内存不足”消息。当我关闭记录集(或连接,之前有或没有取消)时,ADO 2.8 会等待,直到所有记录都被提取。

            我不知道 ADO.NET 是否做得更好,但我认为在取消/关闭后监控内存和网络访问以确保 ADO 真的停止读取数据是个好主意。

            【讨论】:

              【解决方案9】:

              KILL SESSION 是我取消长时间运行的查询的唯一有效方法。我正在使用提供的托管 oracle,并且 OracleCommand.Cancel() 有时可以工作,但通常它不起作用。根据我的测试,OracleCommand.CommandTimeout 也不受尊重。前段时间,当我使用非托管 oracle 时,我设法取消了命令,但不再使用托管的。以任何方式终止会话是唯一的选择。查询不在 UI 线程上运行,而是在单独的线程上运行。取消命令是从 UI 线程发送的。它有点复杂,因为应用程序使用了一个使用 WCF 的中间层,但最终我要终止会话。当然,在运行查询时,我必须找到并保存会话,以便在必要时将其终止。有很多方法可以找到 sid 和 serial# 以终止 oracle 会话,我想浪费您的时间来解释您已经知道的事情。

              【讨论】:

                【解决方案10】:

                Oracle 在 18c 中引入了ALTER SYSTEM CANCEL SQL。您需要在 SQL 中添加某种带有 UID 的注释,然后查找类似这样的内容

                SELECT S.SID||','||S.SERIAL#
                                    FROM GV$SESSION S, V$SQL Q
                                    WHERE S.USERNAME IS NOT NULL
                                    AND S.STATUS = 'ACTIVE'
                                    AND S.SQL_ID IS NOT NULL
                                    AND Q.SQL_ID = S.SQL_ID
                                    and sql_text like '%{queryId}%'
                

                然后从 .NET ALTER SYSTEM CANCEL SQL 'SID, SERIAL' 运行另一个操作

                【讨论】:

                  【解决方案11】:

                  我认为这是不可能的。以下是 Oracle 网站上有关此主题的讨论的链接: http://forums.oracle.com/forums/thread.jspa?threadID=400492&start=15&tstart=0

                  【讨论】:

                  • 这是完全错误的 - 请参阅下面的评论。这绝对是可能,因为我已经看到它完成了。不过,我希望我知道如何......
                  • 我希望这个帖子的原作者取消将此答案标记为已接受,如果 JosephStyons 说这是可能的。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-04-03
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-10-29
                  • 2012-01-04
                  • 2011-03-03
                  相关资源
                  最近更新 更多