我真的需要使用“SET XACT_ABORT ON”吗?

时间:2009-05-27 20:08:06

标签: sql-server sql-server-2005 error-handling transactions

如果你小心并且在所有事情上使用TRY-CATCH,并且你真的需要使用错误回滚:

SET XACT_ABORT ON

换句话说,是否有任何TRY-CATCH会错过SET XACT_ABORT ON会处理的错误?

6 个答案:

答案 0 :(得分:35)

请记住,TRY-CATCH无法使用或不使用XACT_ABORT进行捕获。

但是,SET XACT_ABORT ON不会影响错误的捕获。它确实保证任何事务都回滚/注定失败。当“关闭”时,您仍然可以选择提交或回滚(受xact_state约束)。这是SQL 2005对XACT_ABORT

的主要行为更改

如果客户端命令超时启动并且客户端发送“abort”指令,它还会删除锁等。如果没有SET XACT_ABORT,则在连接保持打开状态时可以保留锁定。我的同事(MVP)和我在今年年初对此进行了彻底的测试。

答案 1 :(得分:3)

我认为执行分布式事务时需要SET XACT_ABORT ON。

From the books on line: 对于大多数OLE DB提供程序(包括SQL Server)的隐式或显式事务中的数据修改语句,必须将XACT_ABORT设置为ON。不需要此选项的唯一情况是提供程序是否支持嵌套事务。有关更多信息,请参阅分布式查询和分布式事务。

答案 2 :(得分:1)

XACT_ABORT确实会影响错误处理:它会在遇到错误时中止整个批处理,并且产生错误的行后面的任何代码(包括错误检查!)将永远不会执行。此行为有两个例外:XACT_ABORT被TRY ... CATCH取代(CATCH块将始终执行,事务不会自动回滚,只能呈现不可承受),XACT_ABORT将忽略RAISERROR。

答案 3 :(得分:1)

我的理解是,即使使用try catch并且在catch块中没有使用rollback语句,当XACT_ABORT为ON时,任何不可提交的事务都将被回滚。

答案 4 :(得分:1)

盲目地总是使用SET XACT_ABORT ON;这最近烧了我。

我在StackOverflow上阅读了一个令人信服的论点,建议你 总是使用XACT_ABORT ON。我更改了系统以在连接期间设置该选项。除了它导致数据损坏和许多痛苦

begin transaction
try
    perform insert
    catch duplicate key violation and react appropriately

    perform more actions

    commit transaction
catch
    rollback transaction
end

除了您的“更多操作”将不再在交易中发生。因为即使您发现重复密钥违规,服务器也不再处于事务中:

begin transaction
try
    perform insert
    catch duplicate key violation and react appropriately
    transaction implicitly rolled back

    perform more actions

    commit transaction -> fails because not in a transaction
catch
    rollback transaction -> fails because not i a transaction
end

我已经扭转了自己。 从不使用SET XACT_ABORT ON


修改:人们似乎认为问题来自于尝试在不在交易中时调用ROLLBACK TRANSACTION。如果交易没有进行,他们认为可以通过不调用ROLLBACK来解决问题。

让我们使用一些伪代码,更改名称以保护NDA:

const
   SQLNativeErrorPrimaryKeyViolation = 2627; //Primary keys. 2601 is for other unique index

void x(String sql)
{
   database.Connection.ExecuteNoRecords(sql);
}

这是使这个答案更具可读性的迂腐方式;我们使用x来表示某些SQL语句的执行:

void DoStuff()
{
   x("BEGIN TRANSACTION");
   try
   {
      try
      {
         x("INSERT INTO Patrons (AccountNumber, Name, Gender)"+
           "VALUES (619, 'Shelby Jackson', 'W'"); 
      } 
      catch (ESqlServerException e)
      {
         //check if the patron already exists (or some other hypothetical situation arises)
         if (e.NativeError == SQLNativeErrorPrimaryKeyViolation)
         {
            //This patron already exists. Set their frob to grob because contoso the blingblong
            x("UPDATE Patrons SET Frob='Grob' WHERE AccountNumber = 619");

            //20110918: Dont forget we also need to bang the gardinker
            x("EXECUTE BangTheGardinker @floof=619");
         }
         else
            throw e;
      }

      //Continue with the stuff
      x("EXECUTE Frob('{498BBB4D-D9F7-4438-B7A6-4AB5D57937C0}')");

      //All done, commit the transaction
      x("COMMIT TRANSACTION");       
   }
   catch (Exception e)
   {
      //Something bad happened, rollback the transaction 
      //(if SQL Server didn't kill the transaction without our permission)
      x("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION");


      throw e;
   }
}

XACT_ABORT ON很酷,让我们用它

所以,该代码有效。如果我们期望出现错误,我们会处理并继续。这称为处理错误。如果发生了一些未知异常(我们没想到的话),我们rollback可能正在进行的任何交易。

现在让我们看看我们是否盲目遵循XACT_ABORT始终应该遵循的建议:

 DbConnection Connection()
 {
    if (_connection == null)
    {
       _connection = new SqlConnection();

       //It is generally recommended that you always have xact_abort on.
       //If a connection is closed with a transaction still in progress
       //it still leaves locks held until that connection is finally recycled
       //Also, when querying linked severs in a client-side transaction, the
       //operation won't work until xact_abort is on (SQL Server will throw an saying xactabort is off
       _connection.ExecuteNoRecords("SET XACT_ABORT ON");
    }

    return _connection;
 }

void x(String sql)
{
   database.Connection.ExecuteNoRecords(sql);
}

您是否看到 DoStuff 会导致损坏?

正确编写

DoStuff 以处理错误情况。但是,XACT_ABORT ON引入连接现在会导致数据库损坏。对于那些没有看到错误的人,让我们来看看代码:

void DoStuff()
{
   x("BEGIN TRANSACTION");
   try
   {
      try
      {
         x("INSERT INTO Patrons (AccountNumber, Name, Gender)"+
           "VALUES (619, 'Shelby Jackson', 'W'"); 

      } 
      catch (ESqlServerException e)
      {
         //WARNING: WE ARE NO LONGER OPERATING IN A TRANASCTION
         //Because XACT_ABORT is on, the transaction that we started has been implicitly rolled back.
         //From now on, we are no longer in a transaction. If another error happens
         //the changes we make cannot be rolled back

         //check if the patron already exists (or some other hypothetical situation arises)
         if (e.NativeError == SQLNativeErrorPrimaryKeyViolation)
         {
            //WARNING: This update happens outside of any transaction!
            //This patron already exist. Set their frob to grob because contoso the blingblong
            x("UPDATE Patrons SET Frob='Grob' WHERE AccountNumber = 619");

            //WARNING: This stored procedure happens outside of any transaction!
            //20110918: Dont forget we also need to bang the gardinker
            x("EXECUTE BangTheGardinker @floof=619");
         }
         else
            throw e;
      }

      //WARNING: This stored procedure happens outside of any transaction!
      //If any error happens from
      //Continue with the stuff
      x("EXECUTE Frob('{498BBB4D-D9F7-4438-B7A6-4AB5D57937C0}')");

      //WARNING: This stored procedure happens outside of any transaction. It will throw:
      //   Msg 3902, Level 16, State 1, Line 1
      //   The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
      //All done, commit the transaction
      x("COMMIT TRANSACTION");       
   }
   catch (Exception e)
   {
      //If there was an error during Frob, we would want to catch it and roll everything back.
      //But since SQL Server ended the transaction, we have no way to rollback the changes

      //And even if the call to Frob (or Updating the patron's Grob, or Banging the Gardinder)
      //didn't fail, the call to COMMIT TRANSACTION will throw an error

      //Either way, we have detected an error condition that cannot be rolled back in the database


      //Something bad happened, rollback the transaction 
      //(if SQL Server didn't kill the transaction without our permission)
      x("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION");


      throw e;
   }
}

正确编写并且正常运行的代码会损坏,导致错误,最坏的情况会导致数据库损坏。所有这些都是因为我打开了XACT_ABORT ON

答案 5 :(得分:0)

当XACT_ABORT在触发器中设置为OFF并且我在触发器主体中调用RAISEERROR时,更改未回滚。