如何从KILLED / ROLLBACK状态中抢救SQL Server 2008查询?

时间:2010-05-12 16:02:17

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

我有一个存储过程,它将从特定查询中出现的数百万行批量插入到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,对吗?)

在任何情况下,我的过程只是坐在那里,死了,没有响应杀戮,并锁定桌子。这非常令人沮丧,因为我想继续开发我的查询,而不是在假装完成假定的回滚时等待我的服务器停滞不前的时间。

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

6 个答案:

答案 0 :(得分:10)

一些评论。

首先,关于无法取消正在进行的回滚,gbn是正确的。这就是SQL如何保持事务完整性,并且您不会希望更改该行为。如果您完全不在乎并且只是想让您的数据库恢复到最后一次备份时的状态,那么请按照他的步骤进行操作。

但是,有一点需要注意。有些时候我已经看到spid 没有真正回滚的地方,它只是卡住了(通常是0%或100%的进度)。在这种情况下,最可靠的指标是,spid in activity监视器的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的小批量记录,并在每个批次之后提交。

答案 1 :(得分:9)

它正在回滚交易。

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

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

如果你想解决它:

  • 停止SQL Server
  • 删除数据库文件
  • 启动SQL Server
  • 将数据库处于崩溃状态<​​/ li>
  • 恢复

YMMV当然: - )

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

据我所知,通过做一些令人讨厌的事情(如硬重置)可能会破坏数据文件的一致性,然后SQL将进入恢复状态并仍然执行回滚的各个方面以确保回滚事务是成功回滚。

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

答案 4 :(得分:1)

当您终止SQL Server进程时,它不会立即死亡,必须首先回滚该pocesses'活动事务所完成的所有工作。回滚可能需要相当长的时间 - 可能与查询在kill之前执行时使用的时间一样多,甚至更多。

我还看到了一个错误,其中一个被杀死/回滚的过程无限期地存在。幸运的是,在我看到这个案例的情况下,该过程没有重要的锁定!

您可以采取一些措施来避免这种情况,但我不想在不了解您的要求和情况的情况下推荐这样的事情,因为它可能会对其他程序/查询产生不利影响。

答案 5 :(得分:0)

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

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

反向逻辑 - SPID阻止的SPID阻止SPID回滚。

SQL for you:)