事务死锁分析1
张晓林 发表于2010年06月02日 10:31
阅读(3) 评论(
0)
分类:
【表A】与【表B】之间有外键约束(具体怎么约束的无所谓,因为外键和事务死锁没有绝对关系)。【表A】=主键表,【表B】=外键表。 公司有几位程序员写的代码总是出现死锁,现在将事务死锁情况重现.
using
(事务) {
try
{
for
( )
//
一个循环
{
if
(查询【表A】有该【记录】
==
false
)//这个查询没有用当前事务的数据库连接,而是新开一个数据库连接查询数据库 { 将【记录】插入【表A】; 插入【表B】; } } 事务.提交(); }
catch
{ 事务.回滚(); } }
4月25日早9:00补充:以上代码逻辑如果放在一个存储过程里面,是没有问题的。但是放在C#中,程序员稍不注意,就很可能出现问题。出现问题的关键地方在“查询【表A】”。C#中,如果查询【表A】和当前的事务不是同一个数据库连接,而是新开一个连接,就会死锁(可以看留言中我的回复)。所以:要么 1,查询【表A】改为使用当前事务的数据库连接(推荐用这种方法) 2,要么按下面的进行代码改造,将事务移到循环体内。 当循环只有1次的时候,上面代码运行没有问题。 一旦循环大于1次的时候,死锁立即出现,运行SQL Server 2005的sp_who_lock,发现死锁的地方正是“查询【表A】”这块。为什么呢? 因为:第1次循环,插入【表A】后,事务将【表A】设置了独占锁,但是第1次循环完后,事务并没有提交,也就没有解开【表A】上的独占锁。因为表A被独占锁了,所以第2次循环时,“查询【表A】”这个操作进行不下去(后面的插入【表A】更是如此),一直在等待事务提交以解开锁,但是事务运行到第2次循环的查询【表A】就死了,循环无法继续进行,也就不能运行到循环外边的事务.提交(),【表A】的独占锁永远没法解开。死锁就这样产生了。
既然知道了死锁的原因是因为循环里面没有解开独占锁,所以我们应该把事务.提交()放置在循环内。另外根据事务体的逻辑尽量少的原则,我们把事务的声明移植循环体内,使事务体的代码行数尽量少。代码如下。
for
( )
//
一个循环
{
bool
有记录
=
查询【表A】有该【记录】;
//
将这个查询移出事务是良好的编码习惯,虽然这里不必要
using
(事务) {
try
{
if
(有记录
==false
) { 将【记录】插入【表A】; 插入【表B】; } 事务.提交(); }
catch
{ 事务.回滚(); } } }
经过以上改造之后,几位程序员写的业务运行正常。
由此总结: 1,在C#等程序代码中使用事务,并在事务内进行查询的时候,特别要小心,确保该查询和事务使用的是同一个数据库连接。防止表被独占死锁。 2,事务体内应尽可能少的逻辑,尽可能少的代码行数。 3,4月25日9:00补充:防止事务死锁最好的方法,还是大家推荐的用存储过程,在存储过程里面使用事务(只不过门槛稍高,手工活儿稍多)
|
转载于:https://www.cnblogs.com/heziyan/archive/2010/08/26/1808826.html