加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-15 23:34:56 所属栏目:百科 来源:网络整理
导读:我有以下代码(简化)来访问我的数据库.我知道使用语句和参数但我缩短了这一点以集中我的问题. string ConnectionString = "ConnectionStringHere";//1. insert TransactionSqlConnection myConnection = new SqlConnection(ConnectionString); SqlTransaction
我有以下代码(简化)来访问我的数据库.我知道使用语句和参数但我缩短了这一点以集中我的问题.

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选择死锁牺牲品.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读