程序运行一段时间后,MSSQL上的Billno表不能访问,是死锁、阻塞还是并发导致的?(200)

  • 主题发起人 主题发起人 Lessy
  • 开始时间 开始时间
L

Lessy

Unregistered / Unconfirmed
GUEST, unregistred user!
我在企业管理器上的锁查到以下信息,--------------------------------------------------------------------------进程ID | 锁类型 | 模式 | 状态 | 所有者 | 索引 |资源 |58 | TAB | IX | GRANT | Xact | | |58 | KEY | X | GRANT | Xact | PK_Billno |(0302fc21f1a)|58 | PAG | IX | GRANT | Xact | PK_Billno |1:1773 |--------------------------------------------------------------------------查看此进程属性时,是一句Insert到Billno表的sql语句Billno是我用来记录单据的最大编号,所以几乎每开单据的窗口都会访问此表来获取一个新的单据编号当上述情况出现后,前台的客户端在新增时就会超时,由于Billno不能访问导致,如果此时把进程58取消后,又可以了,但过一段时间又会出现,真搞不懂为什么我现在已经使用事务来获取新的单据编号,但情况还是一样,不知道这种情况是死锁还是阻塞,还是客户端太多并发导致的?客户端数量不超过30个,一般情况下是10个客户端左右。大家有什么好的解决方法吗?系统结构:三层,中间层通过ADO访问数据库,事务也是用ADO控制的,客户端都是通过中间层的函数来获取新编号的
 
取单据号的实现方法有问题。
 
以下是其中一个获取单号的代码,我自己写了一个工具测试获取单号,同时打开10个,一起不停地获取单号,但也没有出现那种情况,但在客户那里就会function TMyData.GetPlanNO: OleVariant;Var Years, Months, Days: Word; _Months,_maxno:string;//_Months 格式为 1+月份+日 A1:TADOQuery; IsInTrans:boolean;//这个变量是我前天才加上去的,本来这个过程我没有用事务,加 //上去是用于判断是否已开启事务,如果已开启,则不处理, //反之先开启事务再获取单号 Begin try try IsInTrans:=false; if ADOConnection.InTransaction then IsInTrans:=true else ADOConnection.BeginTrans; A1:= TADOQuery.Create(nil); A1.Connection:=IOConnect; //输出格式:PR+maxno 长度15 DecodeDate(Now, Years, Months, Days); _Months:='1'+ formatfloat('00',Months)+ formatfloat('00',Days); A1.Close; //先查询有没有此单号的记录 A1.SQL.Text:='SELECT Profix,MaxNo,Months,Years FROM BILLNO WHERE Months=' + _Months + ' AND YEARS=' + IntToStr(Years)+' and Profix=''PR'''; A1.Open; //没有此单号的记录则新增 If A1.IsEmpty then begin A1.Close; A1.SQL.Text := 'INSERT INTO BILLNO(Profix,Years,Months,MaxNo) Values (''PR'',' + IntToStr(Years) + ',' + _Months + ',''1'')'; A1.ExecSQL; _maxno:=IntToStr(Years)+formatfloat('00',Months)+ formatfloat('00',Days)+'001'; end else //有此单号的记录则update begin _maxno:=IntToStr(Years)+formatfloat('00',Months)+ formatfloat('00',Days)+ formatfloat('000',A1.fieldbyname('MaxNO').AsFloat+1 ); A1.Close; A1.SQL.Text := 'Update BILLNO set MaxNO=MaxNO+1 where Profix=''PR'' and Years='+IntToStr(Years)+' and Months='+_Months; A1.ExecSQL; end; _maxno:=copy(_maxno,3,length(_maxno)); Result:= 'PR'+_maxno; if not IsInTrans then ADOConnection.CommitTrans; except if not IsInTrans then ADOConnection.RollbackTrans; end; finally A1.Free; A1:=nil; end;
 
高手给我一个指引吧,我列出的那个表格,到时是阻塞还是怎么回事?
 
通常是死锁,一个事物还未提交,另一个事物访问相同资源而引起的.
 
楼上的,你一般怎样解决并发问题呢?
 
取单号效率太低.楼主陷入了盲目封装逻辑到中间层的错误了.其实,完全可以一个存储过程在后台执行即可.
 
并发问题-->要分两种情况来处理:1>.非修改性的读写(比如输入时),这里要允许脏读.2>.修改,或修改前的判断(比如判断库存量是否负数),要禁止脏读.不断地切换,不要怕麻烦.这才是最好的方法.
 
取单号效率低?此话怎讲?把所有去单号的过程都写到存储过程就不会出现死锁吗?我现在就只是取单号有问题,库存控制方面暂时没出现这个问题
 
我刚才想到的办法是让取单号的函数每次只允许一个客户端取,如果同时多个客户端一起取单号,则等待另一个结束了再取单号,不知道这个方法可行不?
 
我明白你说的效率低了,不知道能否赐教如何改进,把效率提高?
 
请教aKnightChen:一般什么样的逻辑不要封装在中间层?
 
是不是阻塞,企业管理器里可以明显看出来。会有正提示“正在阻塞中”你上面的取单号的程序是错误的,请查询离线数据,里面有相应的方法。
 
if not IsInTrans then ADOConnection.CommitTrans; 这里有问题. 因为如果IsInTrans = true 时, 你取billno的任务就一直不会提交. 就会发生死锁. 但是你的测试却立即提交了. 所以测试不出来.
 
3868474: IsInTrans:=false; if ADOConnection.InTransaction then IsInTrans:=true else ADOConnection.BeginTrans;这一段就判断了是否在执行事务,如果已开始事务,则不提交,让其它过程提交事务如果没有,则执行事务
 
我现在已经把取单号的任务交给存贮过程了,昨晚更新到客户的系统,而且检查了企业管理器确认里面没有死锁,但今天早上9点左右就出现了阻塞(出现了WAIT状态),怎么让存贮过程执行也会这样?
 
IsInTrans:=false; if ADOConnection.InTransaction then IsInTrans:=true else ADOConnection.BeginTrans; if not IsInTrans then ADOConnection.CommitTrans;我说的就是这个问题. 因为你在取billno之前, 数据库连接可能已经处于事务中了, 所以这里的ADOConnection.CommitTrans是有可能不执行的.
 
同样的道理, 如果在存储过程调用之前就有未提交的事务,那么系统同样会发生死锁. 你需要做的是, 尽可能的及时提交事务, 最短时间的占用资源.
 
多人接受答案了。
 
后退
顶部