mat*_*uez 6 c# sql-server transactionscope read-committed-snapshot
最近我不得不解决锁定问题,比如事务已经锁定资源与另一个进程死锁,并被选为死锁受害者.重新运行该交易.
在阅读了几篇文章并分析了我的系统背景后,我最终接受了最常用的解决方案:
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
Run Code Online (Sandbox Code Playgroud)
上面的示例按预期工作.这告诉我数据库配置正确.
我的问题在于C#代码.虽然我能够防止锁定情况(READ_COMMITTED_SNAPSHOT正在工作),但我无法在我的c#代码上复制"允许快照隔离"行为.我尝试使用TransactionScope而没有它.我的目标是让它与TransactionScope一起使用.
我对C#的测试是启动一个长事务:从我的表中读取值,等待20秒,再次读取值并打印两个值.当代码休眠20秒时,我转到SQL Server Management Studio并将值更新为新值.20秒后,显示原始值和新值.由于ALLOW_SNAPSHOT_ISOLATION和SET 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);
}
Run Code Online (Sandbox Code Playgroud)
没有TransactionScope:
...
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);
}
Run Code Online (Sandbox Code Playgroud)
有任何想法吗?
我正在使用.Net 4.5,Dapper 1.50.2和SQL Server 2014
更新1
我能够在非 TransactionScope版本上使用Snapshot隔离:
using (var transaction = connection.BeginTransaction(IsolationLevel.Snapshot))
Run Code Online (Sandbox Code Playgroud)
但是我仍然需要它来处理TransactionScope版本.
| 归档时间: |
|
| 查看次数: |
314 次 |
| 最近记录: |