有没有办法在不更改(或添加)索引的情况下避免更新查询死锁?
以下查询始终生成死锁
update table1
set Batch_ID=1
where item_id in (select top 300 t1.item_id
From table1 t1 inner join table2 t2 on t1.item_id=t2.item_id
inner join table3 t3 on t1.item_ID=t3.item_ID
Where IsNull(t3.item_Delivered,0) = 0
And t1.TBatch_ID is Null
And t2.Shipper_ID = 2
And DateDiff(day,t1.TShipping_Date,getdate()) < 90
And (
DateDiff(minute,IsNull(t1.LastTrackingDate,DateAdd(day,-2,GetDate())),getdate()) > 180
OR (DateDiff(minute,IsNull(t1.LastTrackingDate,DateAdd(day,-2,GetDate())),getdate()) > 60 And IsNull(t3.item_Indelivery,0) = 1)
)
And t2.Customer_ID not in (700,800)
Order By t1.LastTrackingDate, t2.Customer_ID)
通常我在select query(reader)上使用set transaction isolation level read uncommitted
,但在这种情况下它是一个更新查询(writer)。所以我不能应用相同的推理(隔离级别)。
有没有办法为子查询设置事务隔离级别(仅适用于select)??
我可以为子查询的select子句中的每个表添加WITH(NOLOCK)吗?
由于
答案 0 :(得分:0)
查询似乎在满足特定条件的前300行中将Batch_ID
列从NULL切换为1。
此更新容易出现死锁,因为如果两个连接同时运行相同的查询,则两个查询都会找到重叠的table1
行,并且两者都会尝试更新(在返回的行之间存在竞争条件)子查询和外部更新)。
Re:(NOLOCK)
- 不,读取未提交将导致更加不可预测的行为。一种选择是通过提高锁定悲观性来同步对更新的并发调用,这样任何并发连接都将被阻塞,直到第一个连接的批处理300完成标记为止,例如:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
update table1
set Batch_ID=1
where item_id in (select top 300 t1.item_id
From table1 t1 (WITH XLOCK) ...
SET TRANSACTION ISOLATION READ COMMITTED;