【问题标题】:What is the benefit of using "SET XACT_ABORT ON" in a stored procedure?在存储过程中使用“SET XACT_ABORT ON”有什么好处?
【发布时间】:2010-11-12 02:39:31
【问题描述】:

在存储过程中使用 SET XACT_ABORT ON 有什么好处?

【问题讨论】:

标签: sql sql-server


【解决方案1】:

SET XACT_ABORT ON 指示 SQL Server 在发生运行时错误时回滚整个事务并中止批处理。它涵盖了客户端应用程序而不是 SQL Server 本身发生的命令超时等情况(默认的XACT_ABORT OFF 设置不涵盖。)

由于查询超时将使事务保持打开状态,因此建议在所有具有显式事务的存储过程中使用SET XACT_ABORT ON(除非您有特定的理由不这样做),因为应用程序在打开的连接上执行工作的后果交易是灾难性的。

Dan Guzman's Blog 有一个非常棒的概述,

【讨论】:

  • 那为什么默认不开启呢?
  • 如果您在 Sql 中有 BEGIN TRY-BEGIN CATCH 和 ROLLBACK 和 BEGIN CATCH 块,是否仍然需要 XACT_ABORT ?
  • @user20358 BEGIN TRY-BEGIN CATCH 不会捕获客户端应用程序上发生的超时之类的事情,并且某些 SQL 错误也是无法捕获的,从而为您留下一个您不会捕获的开放事务期待一个。
【解决方案2】:

在我看来,SET XACT_ABORT ON 已被 SQL 2k5 中添加的 BEGIN TRY/BEGIN CATCH 淘汰。在 Transact-SQL 中的异常块之前,处理错误确实很困难,并且不平衡的过程太常见了(与入口相比,在退出时具有不同 @@TRANCOUNT 的过程)。

通过添加 Transact-SQL 异常处理,编写正确的过程来保证正确平衡事务变得更加容易。例如我使用这个template for exception handling and nested transactions:

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

它允许我编写原子过程,在出现可恢复错误时只回滚自己的工作。

Transact-SQL 程序面临的主要问题之一是数据纯度:有时接收到的参数或表中的数据完全错误,导致重复键错误、引用约束错误、检查约束错误等等。毕竟,这正是这些约束的作用,如果这些数据纯度错误是不可能的并且全部被业务逻辑捕获,那么这些约束将全部过时(为了效果而添加了戏剧性的夸张)。如果 XACT_ABORT 为 ON,那么所有这些错误都会导致整个事务丢失,而不是能够编写优雅地处理异常的异常块。一个典型的例子是尝试执行 INSERT 并在 PK 违规时恢复为 UPDATE。

【讨论】:

  • 除了客户端超时...我的观点是 SET XACT_ABORT 在 SQL 2005 中更有效,因为行为更可预测:批量中止错误要少得多。
  • 我有点同意,但我会计划处理所有可能发生的错误,因为我知道如果发生命令超时,我将作为开发人员 DBA 承担责任。
  • @RemusRusanu 你会如何处理长时间运行的同步数据库操作?
  • MSDN 文档指出:“对于大多数 OLE DB 提供程序(包括 SQL Server)的隐式或显式事务中的数据修改语句,必须将 XACT_ABORT 设置为 ON。唯一不需要此选项的情况是提供者支持嵌套事务。” msdn.microsoft.com/en-us/library/ms188792(v=sql.120).aspx
  • “在我看来 SET XACT_ABORT ON 已被 BEGIN TRY/BEGIN CATCH 所淘汰” - 我听到了,但请参阅 sommarskog.se/error_handling/Part1.html
【解决方案3】:

引用MSDN:

当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句引发运行时错误,则整个事务将终止并回滚。 当 SET XACT_ABORT 为 OFF 时,在某些情况下,只有引发错误的 Transact-SQL 语句被回滚,事务继续处理。

在实践中,这意味着某些语句可能会失败,从而使事务“部分完成”,并且调用者可能没有这种失败的迹象。

一个简单的例子:

INSERT INTO t1 VALUES (1/0)    
INSERT INTO t2 VALUES (1/1)    
SELECT 'Everything is fine'

此代码将在 XACT_ABORT OFF 的情况下“成功”执行,并在 XACT_ABORT ON 的情况下因错误而终止(不会执行“INSERT INTO t2”,客户端应用程序将引发异常)。

作为一种更灵活的方法,您可以在每个语句之后检查@@ERROR(老派),或使用 TRY...CATCH 块(MSSQL2005+)。我个人更喜欢在没有理由进行某些高级错误处理时将 ​​XACT_ABORT 设置为 ON。

【讨论】:

    【解决方案4】:

    关于客户端超时和使用 XACT_ABORT 来处理它们,在我看来,至少有一个很好的理由让客户端 API (如 SqlClient)出现超时,那就是保护客户端应用程序代码免受 SQL 服务器中发生的死锁代码。在这种情况下,客户端代码没有错误,但必须保护自己免于永远阻塞等待服务器上的命令完成。反之,如果必须存在客户端超时来保护客户端代码,那么 XACT_ABORT ON 也必须保护服务器代码免于客户端中止,以防服务器代码执行的时间超过客户端愿意等待的时间。

    【讨论】:

      【解决方案5】:

      它用于事务管理,以确保任何错误都会导致事务回滚。

      【讨论】:

        【解决方案6】:

        XACT_ABORT ON 监控事务的状态。如果 XACT_STATE =-1 则事务中发生错误。如果 XACT_STATE=1 则事务完成。如果 XACT_State=0 则没有打开的事务。 XACT_ABORT 指定发生错误时是否自动回滚当前事务。

        【讨论】:

        • 抱歉,这不太对。使用 XACT_ABORT OFF,您可能会在事务中遇到错误,但 XACT_STATE 可能仍然为 1,即使出现错误(取决于哪个错误),您可以从中恢复并继续并最终提交——例如,如果您尝试插入重复键。而在同样的场景中,XACT_ABORT ON 将导致 XACT_STATE 为 -1 并阻止您继续和提交事务。 (为了记录,我认为 XACT_ABORT ON 本来是一个更安全的默认值,但这确实描述了行为)。
        猜你喜欢
        • 2016-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-19
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多