如解决数据库死锁?

zhangyong21 2003-07-17 09:56:16
在POS销售系统的前台程序中,结算时需要更新四个表,造成数据库表死锁,数据库为SQL2000,PB开发.各位大侠请帮帮忙!急.
...全文
59 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
anakine 2003-07-21
  • 打赏
  • 举报
回复
你一开始的程序里sqllocal没有设置autocommit为false.问题就在这.
alwaystar 2003-07-18
  • 打赏
  • 举报
回复
上面的代码没有仔细看:)
解决死所的个人观点:
1、不要在一个事物中频繁对相互关联的表操作
2、两个事物中最好不要同时对同一表操作
3、尽量使用insert、delete替代update
等等

同时这么长的后台计算最好放在过程里执行
紫炎圣骑 2003-07-18
  • 打赏
  • 举报
回复
哇,你的程序好长好长呀,首先建议一下以后这么长的程序最好分成多个函数,这样一是看起来比较好看,二是跟踪的时候也好跟踪呀!

大致看了一下你的程序!问一个问题:你每次执行时都出现死锁现象还是偶尔出现?
flyerlxg 2003-07-18
  • 打赏
  • 举报
回复
从前到后,只有最后两个commit,是不是处理时间而导致任务过多,从而产生了"死锁"(有可能不是死锁,但类似死锁)呢?能不能在需要的时候就提交事物呢。如果不行的话,那就尽量用存储存过程来解决在客户端需要运算的工作量,数据传输少,效率应该高一点吧。
sdav 2003-07-18
  • 打赏
  • 举报
回复
SQLCA.AutoCommit = true
把它设置成自动锁定就可以了,别的不用管;
qiyousyc 2003-07-18
  • 打赏
  • 举报
回复
在所有的数据窗口中和修改操作中,加同样的排序条件就可以了。
zhangyong21 2003-07-18
  • 打赏
  • 举报
回复
下面是前台更新程序

//////////////////////////////////////////////////////////////////
////
//// --数据存盘(保证用一个事物,避免死锁)
//// --出现的问题:数据结算时产生死锁现象
/// --原因分析: 程序算法不合理,数据库长时间占有事物不释放资源
/////////////////////////////////////////////////////////////////

sqlthis.autocommit=false
if dw_1.update(true , false ) <> 1 then //salepay表更新
rollback using sqlthis;
messagebox("提示1","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if

//================付款方式为储值卡时数据的更新(脱机状态不允许使用储值卡)
if netlink = true then
string ls_chu , ls_type , ls_t
decimal le_c2,le_c3 , le_c
int jj
for jj = 1 to dw_1.rowcount()
ls_type = dw_1.object.paystyle[jj]
ls_chu = dw_1.object.paystyleno[jj]
le_c = dw_1.object.pay[jj]
select autohandin into :ls_t from paystyle where paystylename = :ls_type using sqlthis ;
if ls_t='1' then // '1'表示储值卡
select card2 , card3 into :le_c2 , :le_c3 from paycard where id = :ls_chu using sqlthis ;
le_c2 = le_c2 - le_c
le_c3 = le_c3 - le_c
update paycard set card2 = :le_c2 , card3 = :le_c3 where id = :ls_chu using sqlthis ;
end if
next
end if
//----储值卡数据处理完毕
//============================================
//=====找零数据的插入
le_return=dw_1.object.c_return[1]
if le_return<0 then
INSERT INTO salepay
( saleid,
salepayno,
seller,
sellername,
saledate,
paystyle,
paystyleno,
currency,
exchangerate,
pay,
payrmb )
VALUES ( :gs_saleid,
:li_rowsum,
:userid,
:username,
:id_system,
'现金',
'',
'人民币',
1,
:le_return,
:le_return )
using sqlthis;

if sqlthis.sqlcode = -1 then
rollback using sqlthis ;
messagebox("提示2","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
end if
//*********付款数据处理完毕********************//

//////////////////////////////////////////////////////////////////
///// 销售明细数据的处理salecom表更新
//////////////////////////////////////////////////////////////////
if w_salemain.dw_1.getitemdecimal(w_salemain.dw_1.rowcount() , "saleprice") = 0 &
or isnull(w_salemain.dw_1.getitemdecimal(w_salemain.dw_1.rowcount() , "saleprice")) then
w_salemain.dw_1.deleterow(w_salemain.dw_1.rowcount())
end if

if w_salemain.dw_1.update(true , false) <> 1 then //salecom表更新
rollback using sqlthis;
messagebox("提示3","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if

decimal le_zk,le_zksum,le_total
le_zk=w_salemain.dw_1.getitemdecimal(1,"saledisc")
le_zksum=w_salemain.dw_1.object.disc[1]
if isnull(le_zksum) then le_zksum =0

le_total=totalval + le_zksum
//----销售主表数据的插入
INSERT INTO salemain
( saleid,
posid,
saledate,
seller,
sellername,
vipid,
totalsale,
totaldisc )
VALUES ( :gs_saleid,
:gs_posid,
:id_system,
:userid,
:username,
'',
:le_total ,
:le_zksum )
using sqlthis ;
if sqlthis.sqlcode=-1 then
rollback using sqlthis ;
messagebox("提示4","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
//---销售主表数据插入完毕
///////////////////////////////////////////////////

/*--更新本地库---*/
if netlink then
UPDATE posmachineparm
SET saleid = :gs_saleid
WHERE posmachineparm.posmachineid = :gs_posid using sqllocal ;
if sqllocal.sqlcode = -1 then //本地库存盘错误时
rollback using sqlthis ;
rollback using sqllocal ;
messagebox("提示5","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
end if

/*------更新网络库-----*/
UPDATE posmachineparm
SET saleid = :gs_saleid
WHERE posmachineparm.posmachineid = :gs_posid using sqlthis ;
if sqlthis.sqlcode=-1 then
rollback using sqlthis ;
rollback using sqllocal ; //前面已经有本地库操作,故本地库事物必须回滚
messagebox("提示6","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
//*POS机销售流水号更新完毕
//=====================================================


//会员卡消费处理
if gi_vip=1 then
update salemain set vipid=:vipid where saleid=:gs_saleid using sqlthis ;
if sqlthis.sqlcode=-1 then
rollback using sqlthis ;
rollback using sqllocal ; //前面已经有本地库操作,故本地库事物必须回滚
messagebox("提示7","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if

select disc,totalamount,totaldisc,totalinterest into :le_disc,:le_totalamout,:le_totaldisc,:le_totalinter from vip where vipid=:vipid using sqlthis;
if isnull(le_disc) then le_disc = 0
if isnull(le_totalamout) then le_totalamout = 0
if isnull(le_totalinter) then le_totalinter = 0
le_tdisc=le_zksum
le_totaldisc=le_tdisc + le_totaldisc
le_totalamout=totalval + le_totalamout
le_totalinter=totalval + le_totalinter
update vip set totalamount=:le_totalamout , totaldisc=:le_totaldisc,totalinterest=:le_totalinter where vipid=:vipid using sqlthis;
if sqlthis.sqlcode=-1 then
rollback using sqlthis ;
rollback using sqllocal ; //前面已经有本地库操作,故本地库事物必须回滚
messagebox("提示8","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
end if
//---会员卡处理完毕

////////////////////////////////////////////
///折让、折扣数据的处理
/////////////////////////////
if w_salemain.dw_disclevel.update(true , false) <> 1 then
rollback using sqlthis ;
rollback using sqllocal ; //前面已经有本地库操作,故本地库事物必须回滚
messagebox("提示9","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
//-------折扣、折让处理完毕
//数据的提交

commit using sqlthis ;
commit using sqllocal ;
///////////////////////////////////////
/////数据处理完毕
/////票据打印
//////////////////////////////////////
zhangyong21 2003-07-18
  • 打赏
  • 举报
回复
这是其中一定需更新的三个表结构,每次锁住salecom表


CREATE TABLE [dbo].[salemain] (
[saleid] [char] (12) NOT NULL ,
[posid] [char] (2) NOT NULL ,
[saledate] [datetime] NOT NULL ,
[seller] [char] (6) NOT NULL ,
[sellername] [varchar] (12) NOT NULL ,
[vipid] [varchar] (12) NOT NULL ,
[totalsale] [decimal](15, 4) NOT NULL ,
[totaldisc] [decimal](15, 4) NOT NULL,
CONSTRAINT salemainpk PRIMARY KEY CLUSTERED
(saleid)
)
go

create index i_salemaindate on salemain
(saledate)
go

CREATE TABLE salecom(
saleid char(12) NOT NULL,
salecomno smallint NOT NULL,
saledate datetime NOT NULL,
comid varchar(12) NOT NULL,
comname varchar(40) NOT NULL,
comtype varchar(10) NULL,
warehouseid varchar(8) NOT NULL,
warehouse varchar(40) NOT NULL,
vendorid varchar(8) NOT NULL,
vendorname varchar(50) NOT NULL,
saleprice decimal(15, 4) NOT NULL ,
saledisc decimal(15, 4) NOT NULL ,
quantity decimal(15, 4) NOT NULL ,
subdisc decimal(15, 4) NOT NULL ,
subsale decimal(15, 4) NOT NULL ,
remark varchar(20) NULL ,
constraint salecompk primary key Nonclustered
(saleid,salecomno)
)
go
create index i_salecomsaleid on salecom
(saleid)
go
create index i_salecomcomid on salecom
(comid)
go
create index i_salecomwarehouseid on salecom
(warehouseid)
go
create index i_salecomsaledate on salecom
(saledate)
go
create index i_salecomsalevendorid on salecom
(vendorid)
go
create table salepay (
saleid char(12) NOT NULL,
salepayno smallint NOT NULL ,
seller char (6) NOT NULL ,
sellername varchar (12) NOT NULL ,
saledate datetime NOT NULL ,
paystyle varchar (20) NOT NULL ,
paystyleno varchar (20) NOT NULL ,
currency varchar (20) NOT NULL ,
exchangerate decimal(15, 8) NOT NULL ,
pay decimal(15, 4) NOT NULL ,
payrmb decimal(15, 4) NOT NULL ,
constraint salepaypk primary key NONclustered
(saleid,salepayno)
)
go
create index i_salepaysaleid on salepay
(saleid)
go

create index i_salepaysaledate on salepay
(saledate)

go
  • 打赏
  • 举报
回复
适时的用
commit;
rollback;
zxthello 2003-07-18
  • 打赏
  • 举报
回复
把表的结构和你的语法给出看看,估计你的更新顺序有问题,违反了约束关系。
zhangyong21 2003-07-18
  • 打赏
  • 举报
回复
这是从修改后的程序目前没有发现死锁
//// --数据存盘(保证用一个事物,避免死锁)
sqlthis.autocommit = false
sqllocal.autocommit = false
if dw_1.update(true , false ) <> 1 then
rollback using sqlthis;
messagebox("提示1","付款数据存盘错误!",stopsign!)

dw_1.setfocus()
return
end if
///// 销售主表数据的处理
if w_salemain.dw_salemain.update(true , false) <> 1 then
rollback using sqlthis;
messagebox("提示2","销售主表数据存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
///// 销售明细数据的处理
if w_salemain.dw_1.update(true , false) <> 1 then
rollback using sqlthis;
messagebox("提示3","销售明细数据存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
///// 会员卡数据的处理
if gi_vip=1 then
if w_salemain.dw_vip.update(true , false) <> 1 then
rollback using sqlthis;
messagebox("提示4","会员卡数据存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
end if
//================付款方式为储值卡时数据的更新(脱机状态不允许使用储值卡)
if netlink = true and dw_paycard.rowcount() > 0 then
if dw_paycard.update(true , false) <> 1 then
rollback using sqlthis ;
messagebox("提示5","储值卡存盘错误!",stopsign!)
return
end if
end if

///折让、折扣数据的处理
if ll_discrowcount > 0 then
if w_salemain.dw_disclevel.update(true , false) <> 1 then
rollback using sqlthis ;
messagebox("提示6","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
end if

//*POS机销售流水号更新开始
//=====================================================
/*------更新网络库-----*/
UPDATE posmachineparm
SET saleid = :gs_saleid
WHERE posmachineparm.posmachineid = :gs_posid using sqlthis ;
if sqlthis.sqlcode=-1 then
rollback using sqlthis ;
messagebox("提示7","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
/*--更新本地库---*/
if netlink then
UPDATE posmachineparm
SET saleid = :gs_saleid
WHERE posmachineparm.posmachineid = :gs_posid using sqllocal ;
if sqllocal.sqlcode = -1 then //本地库存盘错误时
rollback using sqlthis ;
rollback using sqllocal ;
messagebox("提示8","存盘错误!",stopsign!)
dw_1.setfocus()
return
end if
end if
//数据的提交
commit using sqlthis ;
commit using sqllocal ;
zxthello 2003-07-18
  • 打赏
  • 举报
回复
呵呵,吧你的程序整理一下吧,也许自己就好了!好象都是提供可能的原因,没有人愿意看程序,太长了,可读性不好!越是这样越容易出错,还不容易找出来,我认为还是程序的原因,我原来碰到过,当时怎么都不信,可是后来还是找到了,不过费了很长时间和人力!和你的情况很类似。
guofengchs 2003-07-18
  • 打赏
  • 举报
回复
如果你的一次提交还没有结素,如果你的后台程序去RETRIEVE这个表,那么死锁就发生了,按照你前台程序的效率,你的后台程序RETRIEVE的间隔时间要大大增加
zhangyong21 2003-07-18
  • 打赏
  • 举报
回复
死锁之后,所有前台程序都不能进行保存操作,后台程序只要有操作salecom表就会退出程序.
guofengchs 2003-07-18
  • 打赏
  • 举报
回复
你是不是在用那个自动解锁的程序?如果是,建议把监视的时间换成60秒
默认的30秒一次可能会中断正常的大数据量操作。
zxthello 2003-07-18
  • 打赏
  • 举报
回复
程序太长没仔细看,不过死锁还有一个可能:
你的一段数据库操作后有停顿的可能(比如在提交之前还有等待输入,或者点击保存才提交,都有可能),但是因为事务不一致没有提交,
这种情况出现死锁的现象就是表面看来无规律,只要有一台客户端出现上面情况就会锁死。如果你的客户端锁死的时候还存在一台可以操作,那么肯定是这种情况!
另外你的所有更新语句都要判断是否执行成功!你有的语句如update执行后没有判断状态。
zhangyong21 2003-07-18
  • 打赏
  • 举报
回复
非常感谢各位大虾的援助,我正在大刀改造程序,另外我想问一下:后台在时刻监视数据的情况(大概是5秒刷新一次数据库)下是否会产生死锁呢? !!!!
guofengchs 2003-07-18
  • 打赏
  • 举报
回复
你的这个程序设计的不合理,在事物提交之前有太多的操作,特别是UPDATE,机器少的话或者表的数据量少的话可能不会有问题,但是CLNENT数量一多,当一个CLIENT在UPDATE时另外一个用户又去操作那个表的话就发生死锁了,而且你里面还用了几个SELECT,如果你的网络速度不快的话,你这段程序的执行时间会持续几秒,也就是说你的一个事物要开始一段时间才提交,这样很容易产生并发死锁,
当然,我理解你的意思,你想通过最少的提交,来保证事物的完整性。
我有几个建议:
1,程序的结构优化,把费时间的操作比如SELECT语句放在UPDATE之前执行,尽量减少从UPDATE开始到COMMIT的间隔时间。
2. 如果你网络条件不好,或者数据量比较大,最好把这段程序部分写成存储过程。
3. 可以使用触发器来保证事物完整性
4. 网络上有一个程序可以自动解锁,你可以用用,但是只是不得已的方法,因为代价是某个CLIENT和数据库的连接被断开
5. 另外问一下你用的连接方式是什么? OLEDB还是用NATIVE DRIVER
因为如果你用OLEDB最好把AUTOCOMMIT设置为TRUE,否则很容易死锁
希望能有助于你解决问题
另外你程序里面的这段
select autohandin into :ls_t from paystyle where paystylename = :ls_type using sqlthis ;
if ls_t='1' then // '1'表示储值卡
select card2 , card3 into :le_c2 , :le_c3 from paycard where id = :ls_chu using sqlthis ;
le_c2 = le_c2 - le_c
le_c3 = le_c3 - le_c
update paycard set card2 = :le_c2 , card3 = :le_c3 where id = :ls_chu using sqlthis ;

很危险,没有事物的判断,而且容易造成死锁。
紫炎圣骑 2003-07-18
  • 打赏
  • 举报
回复
极有可能是由于客户端同时操作同一表的同一条记录时造成的死锁!!

强烈建议你在客户端更新表的时候对该表的某条记录加锁,即不要出现多个客户端同时更新一个表的同一条纪录!!
举个例子:
//====================================================================
//入库单加锁
//====================================
UPDATE KCRKD1 SET KCRKD1_LSBH=KCRKD1_LSBH WHERE KCRKD1_LSBH=:vsRKLS;
if sqlca.sqlcode(sqlca)<>0 then
vsMessage="对入库单加锁错误!具体信息如下:~r~t"+sqlca.SqlErrText
messagebox("",vsmessage)
end if
这样你就对kcrkd1这个表的一条纪录加锁了,直到你commit以后,该条记录是不允许其他人对其修改的!!!
这样或许就能解决你的死锁问题!!!
hexubing 2003-07-18
  • 打赏
  • 举报
回复
加载更多回复(4)

743

社区成员

发帖
与我相关
我的任务
社区描述
PowerBuilder 脚本语言
社区管理员
  • 脚本语言社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧