如何等待SQL Server数据库回滚完成?

时间:2017-03-31 12:17:37

标签: .net sql-server transactions

使用ADO.NET SqlClient类时,可以通过几种方式回滚事务。通过显式调用SqlTransaction.Rollback,或者在事务范围内执行命令超时或事务超时等。

但是,如何在使用SQL Server(2012或更高版本)时检测此回滚何时完成?对于长时间运行的事务,回滚可能需要很长时间,并且它通常在数据库上非常密集,因此立即重试事务可能不明智。在我们的情况下,我们希望等待回滚完成。

(在我们的特定情况下,由于使用TransactionScope类的事务超时而发生回滚,但我更喜欢一种适用于任何类型回滚的方法。)

我看过最初看起来很有希望的sys.dm_exec_requests。它最初的命令类型为KILL/ROLLBACK,并报告了进度百分比。 (如果我从原始事务中捕获了SPID,我将能够轮询该表并等待它完成)。但是,大约一半时间我注意到它改为AWAITING COMMAND(而回滚仍然在执行)。

我也看了KILL <SPID> WITH STATUSONLY。如果没有正在进行的回滚,这似乎会产生错误,因此它可以用于轮询方法,但我注意到如果我从SSMS运行BEGIN TRAN ... ROLLBACK TRAN批处理,则会报告错误,表示没有回滚当它回滚时正在进行中。在这种情况下,dm_exec_requests表报告ROLLBACK TRANSACTION作为命令类型。

那么如何才能以可靠的方式等待回滚呢?

2 个答案:

答案 0 :(得分:0)

您可以根据spid查看status的交易活动。列rollback将告诉您当前的交易状态,并在完成后从SqlChangeMonitor或消失后更改。

如果您想获得有关.NET的反馈,请使用SqlChangeMonitorexample)来缓存结果并等待rollback通知cmd.Connection.BeginTransaction(IsolationLevel.ReadCommitted)的更改

如果您解决了超时问题,可以查看:

使用 IsolationLevel.ReadCommitted 。这样,您的.NET代码将等待提交或回滚的完成。

cmd类似于SqlCommand的{​​{1}}

答案 1 :(得分:0)

SqlRollback.cs 

using System;
using System.Data.SqlClient;
using System.Threading;

namespace Events
{
    public class SqlRollback
    {
        private string _connString = "Server=(localdb)\\mssqllocaldb;Database=QueryIt.EmployeeDb;Trusted_Connection=true";
        public event EventHandler TransactionRolledBack;

        public void Transact()
        {
            using (SqlConnection conn = new SqlConnection(_connString))
            {
                conn.Open();

                var command = conn.CreateCommand();
                SqlTransaction transaction;
                transaction = conn.BeginTransaction("Transaction");

                command.Connection = conn;
                command.Transaction = transaction;

                try
                {
                    transaction.Save("checkpoint");
                    command.CommandText = "INSERT INTO Employees (name, discriminator) VALUES ('Dimitar', 'Bastun')";
                    command.ExecuteNonQuery();
                    throw new Exception();

                } catch (Exception ex)
                {
                    transaction.Rollback("checkpoint"); //Rolling back to the checkpoint
                    Thread.Sleep(5000); //Simulating some time
                    transaction.Commit();
                    this.OnTransactionRollingBack(EventArgs.Empty);
                }
            }
        }

        private void OnTransactionRollingBack(EventArgs e)
        {
            EventHandler handler = TransactionRolledBack;
            if(handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Events
{
    class Program
    {
        static void Main(string[] args)
        {
            var rollBack = new SqlRollback();
            rollBack.TransactionRolledBack += RollBack_TransactionRolledBack;

            try
            {
                rollBack.Transact();
            } catch (Exception e)
            {

            }
        }

        private static void RollBack_TransactionRolledBack(object sender, EventArgs e)
        {
            Console.WriteLine("Rooled!");
        }
    }
}