【问题标题】:How to salvage SQL server 2008 query from KILLED/ROLLBACK state?如何从 KILLED/ROLLBACK 状态中挽救 SQL Server 2008 查询?
【发布时间】:2010-05-12 16:02:17
【问题描述】:

我有一个存储过程,它将来自某个查询的数百万行批量插入到 SQL 数据库中。它有一个参数选择批次;当省略此参数时,它将收集批次列表并递归调用自身,以便对批次进行迭代。在(伪)代码中,它看起来像这样:

CREATE PROCEDURE spProcedure AS BEGIN
    IF @code = 0  BEGIN
        ...
        WHILE @@Fetch_Status=0 BEGIN
            EXEC spProcedure @code
            FETCH NEXT ... INTO @code
        END
    END
    ELSE BEGIN

        -- Disable indexes
        ...

        INSERT INTO table
        SELECT (...)

        -- Enable indexes
        ...

现在,无论出于何种原因,这个过程可能会很慢:它无法获得锁,它使用的索引之一被错误定义或禁用。在这种情况下,我希望能够终止该过程,截断并重新创建结果表,然后重试。但是,当我尝试终止该过程时,该过程经常渗入 KILLED/ROLLBACK 状态,似乎无法返回。从谷歌我学会了做一个sp_lock,找到spid,然后用KILL <spid>杀死它。但是当我试图杀死它时,它会告诉我

SPID 75:事务回滚 进步。估计回滚 完成度:0%。预计的时间 剩余时间:554 秒。

我确实找到了一个forum message 暗示应该在另一个 spid 可以开始回滚之前杀死另一个 spid。但这对我也不起作用,而且我不明白,为什么会这样......可能是因为我递归地调用我自己的存储过程吗? (但它应该有相同的 spid,对吧?)

无论如何,我的进程只是坐在那里,死了,没有响应杀戮,并锁定了桌子。这非常令人沮丧,因为我想继续开发我的查询,而不是在我的服务器上等待几个小时而假装完成一个假定的回滚。

有什么方法可以告诉服务器不要为我的查询存储任何回滚信息?或者不允许任何其他查询干扰回滚,这样就不会花费这么长时间?或者如何以更好的方式重写我的查询,或者如何在不重新启动服务器的情况下成功终止进程?

【问题讨论】:

    标签: sql-server sql-server-2008 stored-procedures locking


    【解决方案1】:

    几厘米。

    首先,关于无法取消正在进行的回滚,gbn 是正确的。这就是 SQL 保持事务完整性的方式,您不会希望改变这种行为。如果您完全不在乎,只想将数据库恢复到上次备份时的位置,请按照他的步骤操作。

    但是,有一个警告。有时我看到 spid 并没有真正回滚,它只是卡住了(通常处于 0% 或 100% 的进度)。在这种情况下,最可靠的指标是活动监视器中 spid 的 CPU/IO 计数器是否未更改(并且该 SPID 未被另一个 SPID 阻止)。在这种情况下,您可能需要重新启动 SQL 服务(不需要完全重新启动)以清除 spid。

    关于重新组织您的查询以使这些回滚不会削弱您,是的,这是可能的。只需使用显式事务:

        WHILE @@Fetch_Status=0 BEGIN
            BEGIN TRANS
                EXEC spProcedure @code
            COMMIT TRANS
            FETCH NEXT ... INTO @code
        END
    

    数据在每批之后提交。如果您遇到问题并且必须终止 spid,它应该只回滚它正在处理的当前批次。

    即使单个批次太多,您也可以重构您的“spProcedure”以插入 10k-100k 记录的较小批次,并在每条记录之后提交。

    【讨论】:

    • @HLGEM:我会为你做的。 ;)
    • 这就是我喜欢 Stack Overflow 的原因。谢谢你。我确实会重构我的查询,让我在未来为它省去很多的麻烦。
    【解决方案2】:

    它正在回滚事务。

    即使您重新启动实例,它也会继续这样做。

    如果您将 9900 万行插入或删除 1 亿行,则所有 9900 万行都需要回滚。您无法更改此行为。任何单个 DML 语句都是原子的。

    如果你想修复它:

    • 停止 SQL Server
    • 删除数据库文件
    • 启动 SQL Server
    • 删除处于损坏状态的数据库
    • 恢复

    当然是 YMMV :-)

    【讨论】:

      【解决方案3】:

      来过这里。上次对我们来说是处理大约 32 亿条记录。最初的声明在十分钟内完成了 99%;然后在IO上花了20个小时。

      第一次运行大约 8 小时,然后自动备份作业终止了该进程并退回了服务器。花了将近 2 天的时间才让它重新上线,以便我们可以重新开始这个过程。这次确保备份过程已关闭。

      【讨论】:

      • 为什么您的自动备份作业会杀死 spid 并退回服务器??
      • @BradC:该死的好问题,对此我没有明确的答案。他们使用的产品是来自 NetApp 的 Snapshot。其他人注意:不要在高交易系统上使用它。
      • 旧版本的 Oracle 需要停机时间进行备份——我敢打赌,您有一些跨界 DBA,他们不知道在 MS SQL 服务器上不需要这样做。我们有一个不支持“热备份”的​​第 3 方应用程序,即使在 MS SQL 上也是如此!我们一直在和他们争论很多......
      • 仅供参考:Oracle 在线备份出现在版本 7(1992 年!)docs.oracle.com/cd/A57673_01/DOC/server/doc/SCN73/ch23.htm#ol dfile 备份 - 找不到任何较旧的文档,所以它可能在此之前......
      【解决方案4】:

      据我了解,通过执行硬重置等讨厌的操作可能会破坏数据文件的一致性,然后 SQL 将进入恢复状态并仍然执行回滚的各个方面以确保回滚的事务是成功回滚。

      除非像 nolock 这样的东西可以让你通过现有的锁(你没有提到它是否是独占锁)——你可能仍然可以编写表的模式,使其成为 MyTable2——然后继续编写查询并返回完成后修改它们。

      【讨论】:

        【解决方案5】:

        当你杀死一个 SQL Server 进程时,它不会立即死亡,必须首先回滚该进程的活动事务所做的所有工作。回滚可能需要大量时间 - 可能与终止前查询在执行中使用的时间一样多,甚至更多。

        我还看到了一个错误,即终止/回滚进程无限期保留。幸运的是,在我看到的情况下,该进程没有持有任何重要的锁!

        您可以采取一些措施来避免这种情况,但如果不更好地了解您的要求和情况,我不建议这样做,因为它可能会对其他过程/查询产生不利影响。

        【讨论】:

          【解决方案6】:

          同样,一个事务正在执行,用户取消,将 SPID 冻结为 KILLED\ROLLBACK... 不改变 IO / CPU。

          被此 SPID 阻止的其他两个 SPID;我杀的。 因此,锁的争用被移除,允许回滚结束。

          反向逻辑 - SPID 被 SPID 阻止,阻止 SPID 回滚。

          为您提供 SQL :)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多