alter proc 事务A @code nvarchar(30)
as
begin
set rowcount 1
update 表1 with (updlock) set code='xxx' where code=@code
set rowcount 0
end
go
alter proc 事务B @code nvarchar(30)
as
begin
set rowcount 1
update 表1 with (updlock) set TTTcode='xxx' where code=@code
set rowcount 0
end
go
as
begin
set rowcount 1
update 表1 with (updlock) set code='xxx' where code=@code
set rowcount 0
end
go
alter proc 事务B @code nvarchar(30)
as
begin
set rowcount 1
update 表1 with (updlock) set TTTcode='xxx' where code=@code
set rowcount 0
end
go
表1的记录如下:
id code
--- -----
1 'aa'
2 'bb'调用“事务A”的时候, code参数='bb'
调用“事务B”的时候, code参数='aa'假设两个是同时调用的, 假设他们的select时间都很长,假设按照优化原则,只能进行全表扫描
假设“事务A”,“事务B”都同时扫描数据,这样的话,由于共享锁不是互拆的,所以“事务A”,“事务B”都能顺利的对两条记录下共享锁
由于楼主设置的了 "updlock" , 因此, 这两个共享锁都要到提交事务完成后才释放。
假设上面的两步都完成后,“事务A”,“事务B”都准备更新数据,由于更新数据要把共享锁变为排它锁才能更新
而共享锁与排它锁是冲突的, 所以,“事务A”要更新记录2的话,则必须等待“事务B”对记录2下的共享锁的释放
而同样的,“事务B”要更新记录1的话,则必须等待“事务A”对记录1下的共享锁的释放很显然,这样的结果是“事务A”等待“事务B”,“事务B”等待“事务A”,这样就造成了标准的死锁
update 表1 with (updlock) set TTTcode='xxx' where id=@id
不可以直接改的,这里的事务我精减过了
原来可能是看得到ID的同时得到状态再确定UPDATE做不做
如
select @状态=状态 from 表1 with (updlock) where code=@code
if @状态='xxx'
update 表1 with (updlock) set TTTcode='xxx' where id=@id
这样怎么办????难得就没有办法操作先SELECT后UPDATE ????事务隔离级别是默认的read committed啊我吐血
U 更新锁
IU 意向更新锁共享锁
共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。更新锁
更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。排它锁
排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。意向锁
意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。锁模式 描述
意向共享 (IS) 通过在各资源上放置 S 锁,表明事务的意向是读取层次结构中的部分(而不是全部)底层资源。
意向排它 (IX) 通过在各资源上放置 X 锁,表明事务的意向是修改层次结构中的部分(而不是全部)底层资源。IX 是 IS 的超集。
与意向排它共享 (SIX) 通过在各资源上放置 IX 锁,表明事务的意向是读取层次结构中的全部底层资源并修改部分(而不是全部)底层资源。允许顶层资源上的并发 IS 锁。例如,表的 SIX 锁在表上放置一个 SIX 锁(允许并发 IS 锁),在当前所修改页上放置 IX 锁(在已修改行上放置 X 锁)。虽然每个资源在一段时间内只能有一个 SIX 锁,以防止其它事务对资源进行更新,但是其它事务可以通过获取表级的 IS 锁来读取层次结构中的底层资源。
那死锁仍然会存在, 因为这两种事务隔离级别都会导致select的锁在事务结束时才释放
那我想问问,是不是去掉updlock之后,设置为read committed级别,这种SELECT UPDATE就再也不会出现死锁,就是一次也不可能发生,不是说死锁是不可必面的吗???和lock timeout没有关系???
日志如下:
-------------------------------------------------------------
Input Buf: 事务A,'XXX'
SPID: 55 ECID: 0 Statement Type: SELECT Line #: 7(第7行是一个SELECT,全是操作表1)
Mode: S
Input Buf:事务B,'XXX2'
SPID: 69 ECID: 0 Statement Type: UPDATE Line #: 13(第13行是一个UPDATE,全是操作表1)
Mode: IX 事务A大体的内容,事务B基本和事务A相同
--------------------------------------------------------------
ALTER proc 事务A,@code nvarchar(20)
as
set xact_abort on
set transaction isolation level read committed
begin tran
declare @id bigint,@type int
select top 1 @id=id,@type=type from 表1 where xxx='xxx' and code=@code
if @type='a'
update 表1 set code='dddd2' where id=@id
else
update 表1 set code='dddd' where id=@id
if @@error>0
rollback
commit tran
set xact_abort off
--事务 sffitting
ALTER proc sffitting @pro_code nvarchar(30),@lot_no nvarchar(30),@expiry_date smalldatetime,@qty float,@office nvarchar(20),@cust_code nvarchar(20),@ord_no nvarchar(20),@del_date smalldatetime,@gp nvarchar(20)
as
declare @id bigint,@comp_code nvarchar(20)
set xact_abort on
set transaction isolation level read committed
begin tran
if @lot_no='#LOT'
insert into salesdetail(ord_no,pro_code,lot_no,expiry_date,qty,goods_place,status) values(@ord_no,@pro_code,@lot_no,@expiry_date,@qty,@gp,'送货')
else
select top 1 @id=id from productdetail where (last_stat='已收货' or last_stat='初始入库' or last_stat='原厂入库') and hosp_code=@gp and rece_depo=@office and prod_code=@pro_code and prod_batc=@lot_no and prod_qty=@qty
if not @id is null
begin
update productdetail set cust_code=@cust_code,pre_code=last_code,pre_stat=last_stat,last_code=@ord_no,last_stat='送货' where id=@id
update officeproductstocklist set orde_qty=orde_qty+@qty where offi_code=@office and prod_code=@pro_code
insert into salesdetail(ord_no,pro_code,lot_no,expiry_date,qty,goods_place,status) values(@ord_no,@pro_code,@lot_no,@expiry_date,@qty,@gp,'送货')
end
if @@error>0
rollback
commit tran
set xact_abort off--事务Receive
ALTER proc Receive @pro_code nvarchar(30),@lot_no nvarchar(30),@expiry_date smalldatetime,@qty float,@office nvarchar(20),@cust_code nvarchar(20),@ord_no nvarchar(20),@del_date smalldatetime,@status nvarchar(20)
as
set xact_abort on
set transaction isolation level read committed
begin tran
declare @id bigint,@comp_code nvarchar(20)
select top 1 @id=id from productdetail where last_stat='送货' and last_code=@ord_no and prod_code=@pro_code and prod_batc=@lot_no and prod_qty=@qty
if @status='销售'
begin
update productdetail set last_stat='销售' where id=@id
update officeproductstocklist set orde_qty=orde_qty-@qty,sale_qty=sale_qty+@qty where offi_code=@office and prod_code=@pro_code
end
else
begin
if not @lot_no='#LOT'
begin
update productdetail set cust_code=NULL,last_code=pre_code,last_stat=pre_stat where id=@id
update officeproductstocklist set orde_qty=orde_qty-@qty where offi_code=@office and prod_code=@pro_code
end
end
if @@error>0
rollback
commit tran
set xact_abort off
2006-03-23 12:30:09.43 spid1 Node:1
2006-03-23 12:30:09.43 spid1 PAG: 7:1:162558 CleanCnt:2 Mode: IX Flags: 0x2
2006-03-23 12:30:09.43 spid1 Grant List 3::
2006-03-23 12:30:09.43 spid1 SPID: 69 ECID: 0 Statement Type: UPDATE Line #: 13
2006-03-23 12:30:09.43 spid1 Input Buf: Language Event: sffitting 'SF806PC55T','W05J5748','2006-3-14',1,'SH','SH262','SF-S-SH-2006-0903','2006-3-22 10:30:00',''
2006-03-23 12:30:09.43 spid1 Requested By:
2006-03-23 12:30:09.43 spid1 ResType:LockOwner Stype:'OR' Mode: S SPID:68 ECID:0 Ec:(0x35B335A0) Value:0x199c3b40 Cost:(0/0)
2006-03-23 12:30:09.43 spid1
2006-03-23 12:30:09.43 spid1 Node:2
2006-03-23 12:30:09.43 spid1 PAG: 7:1:2889 CleanCnt:2 Mode: S Flags: 0x2
2006-03-23 12:30:09.43 spid1 Grant List 2::
2006-03-23 12:30:09.43 spid1 SPID: 68 ECID: 0 Statement Type: SELECT Line #: 7
2006-03-23 12:30:09.43 spid1 Input Buf: Language Event: Receive 'SF806TS55L120T','W05G3212','2005-8-01',1,'XA','ZX006','SF-S-XA-2006-0270','2006-03-21 17:09:00','销售'
2006-03-23 12:30:09.43 spid1 Requested By:
2006-03-23 12:30:09.43 spid1 ResType:LockOwner Stype:'OR' Mode: IX SPID:69 ECID:0 Ec:(0x1A89D5A0) Value:0x3ff6a280 Cost:(0/228)
2006-03-23 12:30:09.43 spid1 Victim Resource Owner:
2006-03-23 12:30:09.43 spid1 ResType:LockOwner Stype:'OR' Mode: S SPID:68 ECID:0 Ec:(0x35B335A0) Value:0x199c3b40 Cost:(0/0)
而错误日志中, 则是一些具体的值, 似乎是insert的?
日志里显示一个死锁是因为sffitting和receive两个存储过程中的某行,两个存储过程的第7行为select,第13行为update,
不知道为什么还会死唉。
实际上sffitting时应该是这句
select top 1 @id=id from productdetail where (last_stat='已收货' or last_stat='初始入库' or last_stat='原厂入库') and hosp_code=@gp and rece_depo=@office and prod_code=@pro_code and prod_batc=@lot_no and prod_qty=@qty
和receive里的
update productdetail set last_stat='销售' where id=@id
发生了死锁.-------------------------------------------
SPID: 68 ECID: 0 Statement Type: SELECT Line #: 7
2006-03-23 12:30:09.43 spid1 Input Buf: Language Event: Receive 'SF806TS55L120T','W05G3212','2005-8-01',1,'XA','ZX006','SF-S-XA-2006-0270','2006-03-21 17:09:00','销售'
SPID: 69 ECID: 0 Statement Type: UPDATE Line #: 13
2006-03-23 12:30:09.43 spid1 Input Buf: Language Event: sffitting 'SF806PC55T','W05J5748','2006-3-14',1,'SH','SH262','SF-S-SH-2006-0903','2006-3-22 10:30:00',''