好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

c# – 避免SQL事务中的死锁

我有以下代码(简化)来访问我的数据库.我知道使用语句和参数但我缩短了这一点以集中我的问题.

string ConnectionString = "ConnectionStringHere";

//1. insert Transaction
SqlConnection myConnection = new SqlConnection(ConnectionString);            
SqlTransaction myTrans = myConnection.BeginTransaction();                
string sUpdate = "INSERT INTO User (FirstName, LastName) VALUES ('jon','doe')";
SqlCommand myCommand = new SqlCommand(sUpdate, myConnection, myTrans);
myCommand.ExecuteNonQuery();

//2. select from the same table
SqlConnection myConnection2 = new SqlConnection(ConnectionString);
string sSelect = "SELECT FirstName, LastName FROM User WHERE ID = 123";
DataTable dtResult = new DataTable();
SqlDataAdapter myDataAdapter2 = new SqlDataAdapter(sSelect, myConnection2);
myDataAdapter2.Fill(dtResult); //TimeOut Exception here

//submit changes from my transaction here
myTrans.Commit();

在第二部分中,我得到一个TimeOutExcetion,因为在我提交我的事务myTrans.Commit()之前我无法访问我的User表.在那之后 – 僵局.

我的问题是 – 在这里避免死锁的最佳做法是什么?我可以通过制作事务的SELECT部分​​或设置IsolationLevel来避免异常

SqlTransaction myTrans = myConnection.BeginTransaction(IsolationLevel.ReadUncommitted);

但我不确定那些用法.我不必担心无效数据,因为我总是按ID选择.

我没有看到任何理由将SELECT查询作为事务的一部分来解决死锁或超时问题.在您认为的第一个sql连接myConnection上设置ReadUncommitted隔离级别也不是正确的方法.我看到有两种可能的解决方案:

>第一个解决方案:在已启动的事务myTrans上设置隔离级别IsolationLevel.ReadUncommitted将无济于事.如果您对脏读取感到满意,那么您实际上应该在要为User表触发select查询而建立的第二个SQL连接myConnection2上设置此隔离级别.要通过myConnection2为select查询设置隔离级别,您需要使用(nolock)表级提示.所以你的查询将开始如下:

string sSelect = "SELECT FirstName, LastName FROM User WITH (NOLOCK) WHERE ID = 123";

您可以获得更多细节here.

另外,请阅读脏读here的后果,这是使用此特定隔离级别的副作用.

>第二种解决方案:SQL Server的默认隔离级别为Read Committed.因此,当您使用名为myConnection2的变量开始通过新的SQL连接触发查询时,它正在处理ReadCommitted隔离级别. ReadCommitted隔离级别显示的默认行为是阻塞读取,即如果表上存在未提交的更改(由于活动事务可以提交或回滚),则用户表上的select语句将被阻止.它将等待事务完成,以便在回滚时可以读取新更新的数据或原始数据.如果它不执行这样的阻塞读取,那么它将最终执行脏读,这是一个众所周知的数据库并发问题.

如果您不希望SELECT语句被阻止并希望继续使用行的最后一个提交值,则SQL Server中有一个名为READ_COMMITTED_SNAPSHOT的新数据库级别设置.以下是使用SQL脚本更改它的方法:

ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON

引用Pinal Dave的文章here:

If you are having problem with blocking between readers (SELECT) and

writers (INSERT/UPDATE/DELETE), then you can enable this property

without changing anything from the application. Which means

application would still run under read committed isolation and will

still read only committed data.

注意:这是数据库级别设置,将使用READCOMMITTED隔离级别影响数据库上的所有事务.

在我看来,你应该采取第一个解决方案.此外,您应该记住几个关键点,以避免SQL Server查询中的死锁.从here引用Pinal Dave:

>最小化事务和事务时间的大小.>每次在应用程序中始终以相同的顺序访问服务器对象.>避免使用游标,循环或在运行时需要用户输入的进程.>减少应用程序中的锁定时间.>如果可能,使用查询提示来防止锁定(NoLock,RowLock)>使用SET DEADLOCK_PRIORITY选择死锁牺牲品.

查看更多关于c# – 避免SQL事务中的死锁的详细内容...

  阅读:47次