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. ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON 引用Pinal Dave的文章here:
注意:这是数据库级别设置,将使用READCOMMITTED隔离级别影响数据库上的所有事务. 在我看来,你应该采取第一个解决方案.此外,您应该记住几个关键点,以避免SQL Server查询中的死锁.从here引用Pinal Dave: >最小化事务和事务时间的大小.>每次在应用程序中始终以相同的顺序访问服务器对象.>避免使用游标,循环或在运行时需要用户输入的进程.>减少应用程序中的锁定时间.>如果可能,使用查询提示来防止锁定(NoLock,RowLock)>使用SET DEADLOCK_PRIORITY选择死锁牺牲品. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |