【问题标题】:TSQL - Uncaught throw in transaction does not rollback?TSQL - 未捕获的事务不回滚?
【发布时间】:2020-01-10 17:13:21
【问题描述】:

我假设在transaction 中未被捕获的throw 会回滚。但是使用此代码进行测试,似乎交易保持打开状态。这是正常行为吗?

begin transaction;
    throw 50001, N'Exception', 1;
commit transaction;

我使用这个查询:

select * from sys.sysprocesses where open_tran = 1;

列出交易并查看 1 个打开的交易。一旦我在该连接上运行commit,它就会关闭。

那么,当我 throw 之前,我需要一直 rollback 自己吗?如果其他代码在我的事务中但在我的代码之外抛出怎么办?

通常这不是问题,因为关闭连接会结束它。但是如果我sp_getapplock 绑定到transaction,如果我throw 没有手动rollback,它将保持锁定状态。

【问题讨论】:

  • 您描述的行为由 Erland Sommarskog in this article 解释,并要求 XACT_ABORT 为 OFF。批处理已终止,但事务未终止。正如 Erland 解释的那样,仔细的编码和没有长时间运行的事务应该可以减轻这种不幸的行为
  • 你为什么使用sp_getapplock?唯一有效的原因是防止存储过程执行两次。在这种情况下,您必须使用try/catch,而不是让事情顺其自然(也就是环境设置)
  • 这不是一个有效的用途。对于初学者来说,它不能扩展 - 这就像尝试使用 SERIALIZABLE 来确保原子性 - 限制性太强。其次,不能保证它会像您发现的那样开始工作。如果您搜索,您会找到使用适当的锁定提示(如 TABLOCK 和 HOLDLOCK)来实现具有一定性能的队列的方法,但最后,只有破坏性读取(即 DELETE .... OUTPUT deleted....)才是真正安全的。即便如此,还有更安全、更快的分布式排队选项
  • 检查 Remus Rusany 的 this article 使用表作为队列。这是最好的之一。

标签: sql-server tsql


【解决方案1】:

我想你在这里误解了trow。如您所写,它使连接保持打开状态,因为commit transaction; 未执行。 begin transaction;(增加@@TRANCOUNT 计数器),但throw 不会自动减少它! (没有自动回滚)。

throw 被执行时,它会尝试查找catch 语句,如果没有找到则只显示错误,其余的则终止。

引用MSDN

如果 TRY...CATCH 构造不可用,则语句批处理是 终止。异常所在的行号和过程 提出了设置。严重性设置为 16。

在您的简单情况下,您可以像这样扩展它:

BEGIN TRY
    begin transaction;
    throw 50001, N'Exception', 1;
END TRY
BEGIN CATCH
    IF <boolean_expression_for_commit>
       commit transaction;
    ELSE
       rollback transaction;
END CATCH

这将取决于您的用例。 commitrollback 都减少 @@TRANCOUNT 计数器。您将根据&lt;boolean_expression_for_commit&gt; 关闭您的交易。

编辑 OP 想了解更多关于XACT_ABORT

如果XACT_ABORT 设置为ON,则TROW 会导致整个事务回滚,即使CATCH 缺失也是如此。这将减少交易计数器并关闭打开的交易。

然而,一个问题。如果开发人员想要在CATCH 中创建日志,那么也会在CATCH 块上执行回滚! (不创建日志)

SET XACT_ABORT OFF(默认)时,事情会变得很奇怪。根据错误的严重程度,它可能会或可能不会打开事务。如果错误被认为足够严重,它仍然会回滚。在你的情况下,一个简单的THROW 不够严重。

注意:这也是为什么现在应该在RAISERROR 中使用THROW 的原因。 THROW 遵循 XACT_ABORT 设置,RAISEERROR 忽略它。

【讨论】:

  • 添加一些关于xact_abort 的内容,我明天会接受。
  • @CodeAngry 如您所愿,我已添加有关XACT_ABORT 的信息。我在开发时通常使用XACT_ABORT。通常在生产代码中我倾向于使用TRY...CATCH,因为我想记录THROW
  • @CodeAngry 它回答你的问题了吗?
猜你喜欢
  • 1970-01-01
  • 2015-03-02
  • 2016-11-28
  • 1970-01-01
  • 2019-06-09
  • 2017-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多