6

最近我不得不解决锁问题,例如事务在锁资源上与另一个进程死锁,并被选为死锁受害者。重新运行事务。

在阅读了几篇文章并分析了我的系统环境后,我最终接受了最常用的解决方案: ALTER DATABASE MyDb SET READ_COMMITTED_SNAPSHOT ON; ALTER DATABASE MyDb SET ALLOW_SNAPSHOT_ISOLATION ON;

我想要ALLOW_SNAPSHOT_ISOLATION因为这种隔离在我的系统上是有意义的。

我成功实现了https://www.databasejournal.com/features/mssql/article.php/3566746/Controlling-Transactions-and-Locks-Part-5-SQL-2005的“允许快照隔离”部分中描述的流程-Snapshots.htm在 SQL Server Management Studio 中的两个会话上。

伪代码:

SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRAN 1
select value from MyTable where ID=1 --reads original value 

  --On another session:
  BEGIN TRAN 2
  update mytable set value=2 where ID=1
  COMMIT TRAN2

-- back to session 1
select value from MyTable where ID=1 --still reads original value 

上面的示例按预期工作。这告诉我数据库配置正确。

我的问题出在 C# 代码上。虽然我能够防止锁定情况(READ_COMMITTED_SNAPSHOT正在工作),但我无法在我的 c# 代码上复制“允许快照隔离”行为。我尝试使用TransactionScope并没有它。我的目标是让它与TransactionScope一起使用。

我在 C# 上的测试是开始一个长事务:从我的表中读取值,等待 20 秒,再次读取值并打印两个值。当代码休眠 20 秒时,我转到 SQL Server Management Studio 并将值更新为新值。20 秒后显示原始值和新值。由于ALLOW_SNAPSHOT_ISOLATIONSET TRANSACTION ISOLATION LEVEL SNAPSHOT ,我期待这两个原始值

具有事务范围(我正在使用 Dapper):

static TransactionScope CreateTransactionScope()
{
    var transactionOptions = new TransactionOptions();
    transactionOptions.Timeout = TransactionManager.MaximumTimeout;
    transactionOptions.IsolationLevel = IsolationLevel.Snapshot; //also tried IsolationLevel.ReadCommitted
    return new TransactionScope(TransactionScopeOption.RequiresNew, transactionOptions);
}
...
using (var transactionScope = CreateTransactionScope())
{
    T ret;
    using (var connection = new SqlConnection(_connectionString))
    {
        //connection.Execute("SET TRANSACTION ISOLATION LEVEL SNAPSHOT"); this makes no difference
        ret = TestWithTransactionScope(connection);
    }
    transactionScope.Complete();
    return ret;
}
...
public object TestWithTransactionScope(IDbConnection c)
{
    var sql = "select value from MyTable where ID=1";
    var firstRead = c.Query<string>(sql).Single();
    System.Threading.Thread.Sleep(25000);
    var secondRead = c.Query<string>(sql).Single();
    return string.Format("firstRead: {0}, secondRead: {1}", firstRead, secondRead);
}

没有事务范围:

...
using (var connection = new SqlConnection("..."))
{
    connection.Open();
    connection.Execute("SET TRANSACTION ISOLATION LEVEL SNAPSHOT");
    using (var transaction = connection.BeginTransaction())
    {
        try
        {
            var ret = TestWithTransactionScope(connection, transaction);
            transaction.Commit();
            return ret;
        }
        catch
        {
            transaction.Rollback();
            throw;
        }
    }
}
...

public object TestWithTransactionScope(IDbConnection c, SqlTransaction t)
{
    var sql = "select value from MyTable where ID=1";
    var firstRead = c.Query<string>(q, null, t).Single();
    System.Threading.Thread.Sleep(25000);
    var secondRead = c.Query<string>(q, null, t).Single();
    return string.Format("firstRead: {0}, secondRead: {1}", firstRead, secondRead);
}

有任何想法吗?

我正在使用 .Net 4.5、Dapper 1.50.2 和 SQL Server 2014

更新 1

我能够在TransactionScope 版本上使用快照隔离:

using (var transaction = connection.BeginTransaction(IsolationLevel.Snapshot))

但我仍然需要它来处理 TransactionScope 版本。

4

0 回答 0