这里有两部分问题。
我们有一个大表(在 1-2 百万行之间),上面有非常频繁的 DML 操作。在低流量期间,我们有一个代理作业来删除旧行以控制表的大小。它使用下面的 CTE,它在运行时会导致大量阻塞:
;with agent_cte (queue_id) as
(
select top (3000) queue_id
from Table_A with (updlock, readpast)
where state in ('success','error','reject','failed','deleted')
and modify_timestamp <= DATEADD(dd,- 5,getdate())
order by modify_timestamp asc
)
delete from agent_cte
;
我重新编写了它以使用临时表来加速查询并减少阻塞,但我发现使用IN
与EXISTS
确定要删除的行之间存在很大的性能差异。
IN
版本:
-- Create temp Table
create table #Table_AToDelete
(Queue_id uniqueidentifier,
PRIMARY KEY(Queue_id)
);
-- grab top 3k queue_id's older than 5 days, insert into temp table
Insert into #Table_AToDelete
select top (3000) queue_id
from Table_A with (nolock)
where state in ('success','error','reject','failed','deleted')
and modify_timestamp <= DATEADD(dd,- 5,getdate())
-- delete the rows from table_A based on rows in temp table
delete
from Table_A
where queue_id in (select queue_id from #Table_AToDelete)
这个版本在 40-50 秒内运行,但是当我用下面的 delete 语句替换最后一行时:
where exists(select queue_id from #Table_AToDelete)
2分钟后它还在运行,所以我取消了它。
所以,问题:
- 我以前见过用于帮助阻塞和性能的临时表,但不完全理解为什么它会比使用 CTE 执行得更好?我们确实有一个索引
Queue_id
。 IN
为什么delete和delete 中的性能差异很大EXISTS
?
关于如何调整它以更好地执行或进一步减少阻塞的任何反馈?
更多注意事项:
- 使用粘贴计划(上面的链接)可以使用 CTE 和临时表。
- 其他两个具有级联更新和删除的表有 FK,CTE 计划将更多时间花在删除那里,而临时表版本将更多时间花在主表上。
- 一般来说,与 CTE 相比,使用这样的临时表是否有性能优势?
- 我不允许发布表模式,所以如果计划没有足够的信息,我深表歉意。
我还将使用本文中的视图进行测试。
诡异的
两个查询中的性能问题与 CTE 与临时表无关。我看到时间上有差异,但请听我说完。
只是删除
在直接删除中,您花费最多时间从表中删除,而不是从中进行选择。
此查询中的等待都与读取和写入磁盘有关:
#临时表
从 #temp 表中删除的查询计划显示在相同的两个地方花费的时间最多(只是更多时间):
等待此查询的瓶颈与以前相同,只是更多:
差异
考虑到这两个查询都面临硬件困难,我有一半希望时间差异不可重复或不可靠,而更多是偶然事件。
由于它们是可重复的,因此您应该注意对两个查询应用相同的提示。您的第一个查询已
(updlock, readpast)
指定。通过提示跳过行锁readpast
可能是这里出现明显差异的原因。从文档:
由于该锁定提示在这里没有多大意义,请尝试在您的查询中使用
paglock
或tablock
提示以跳过行级锁定。再次注意等待统计信息:
在较慢的查询中,等待计数是较快查询的约 2 倍。
无论如何,您使用的硬件与生产数据库工作负载不兼容。
假设在真实表中还有一个名为queue_id 的列。