我一直在分析一个系统中与一个特别慢的删除操作相关的重复出现的“错误报告”(perf问题)。长话短说:看来
CASCADE DELETE
凯斯负有很大责任,我想知道(a)这是否合理,以及(b)为什么会这样。
我们有一个小部件的模式,比如说,那些小部件位于相关表和相关表的大型图形的根,等等。非常明确地说,不鼓励从这个表中删除;这是“核选择”,用户不会抱有相反的幻想。尽管如此,有时还是必须这样做。
架构如下所示:
Widgets
|
+--- Anvils [1:1]
| |
| +--- AnvilTestData [1:N]
|
+--- WidgetHistory (1:N)
|
+--- WidgetHistoryDetails (1:N)
列定义如下所示:
Widgets (WidgetID int PK, WidgetName varchar(50))
Anvils (AnvilID int PK, WidgetID int FK/IX/UNIQUE, ...)
AnvilTestData (AnvilID int FK/IX, TestID int, ...Test Data...)
WidgetHistory (HistoryID int PK, WidgetID int FK/IX, HistoryDate datetime, ...)
WidgetHistoryDetails (HistoryID int FK/IX, DetailType smallint, ...)
没什么太可怕的,真的。一
Widget
可以是不同的类型,
Anvil
是一种特殊类型,因此关系是1:1(或者更准确地说是1:0..1)。然后是大量的数据-可能有成千上万行
AnvilTestData
每
砧座
随着时间的推移收集,处理硬度,腐蚀,准确的重量,锤子兼容性,可用性问题,并与卡通头部冲击试验。
然后每一个
小装置
对各种类型的交易(生产、库存移动、销售、缺陷调查、RMA、维修、客户投诉等)有着漫长而乏味的历史。根据其年龄,单个小部件可能有10-2万个细节,或者根本没有。
所以,毫不奇怪的是,
级联删除
这里的每一层关系。如果A
小装置
需要删除,这意味着发生了严重的错误,我们需要删除该小部件曾经存在的任何记录,包括其历史记录、测试数据等。再次,核选项。
关系都是索引的,统计数据是最新的。正常的查询速度很快。除了删除之外,这个系统对所有的东西都很平滑。
最后,由于各种原因,我们一次只允许删除一个小部件,因此delete语句如下所示:
DELETE FROM Widgets
WHERE WidgetID = @WidgetID
很简单,看起来无害的删除…
需要2分钟才能跑完
,对于具有
没有数据!
经过艰难的执行计划,我终于能够找出
铁砧试验数据
和
WidgetHistoryDetails
删除为成本最高的子操作。所以我试着关掉
CASCADE
(但是保持实际的fk,只是将其设置为
NO ACTION
)把剧本改写成如下内容:
DECLARE @AnvilID int
SELECT @AnvilID = AnvilID FROM Anvils WHERE WidgetID = @WidgetID
DELETE FROM AnvilTestData
WHERE AnvilID = @AnvilID
DELETE FROM WidgetHistory
WHERE HistoryID IN (
SELECT HistoryID
FROM WidgetHistory
WHERE WidgetID = @WidgetID)
DELETE FROM Widgets WHERE WidgetID = @WidgetID
这两个“优化”都导致了显著的加速,每一个都缩短了将近一分钟的执行时间,因此原来的2分钟删除现在需要大约5-10秒-至少
新的
小部件,没有很多历史或测试数据。
只是说清楚,还有一个
叶栅
从
WidgetHistory
到
WidgetHistory详细信息
,在扇形分叉最高的地方,我只删除了源于
Widgets
.
层叠关系的进一步“扁平化”导致逐渐减少的戏剧性但仍然明显的加速,以至于删除
新的
当所有对较大表的级联删除都被删除并替换为显式删除时,小部件几乎是即时的。
我在用
DBCC DROPCLEANBUFFERS
和
DBCC FREEPROCCACHE
每次测试前。我已经禁用了所有可能导致进一步减速的触发器(尽管这些触发器无论如何都会出现在执行计划中)。我也在测试旧的小部件,同时注意到速度明显加快;删除过去需要5分钟,现在需要20-40秒。
现在我是一个“选择不破碎”哲学的热心支持者,但是对于这种行为似乎没有任何合理的解释,除了
级联删除
关系。
所以,我的问题是:
-
这是SQL Server中DRI的已知问题吗?
(我在谷歌上或这里似乎找不到任何关于这类事情的参考资料;我怀疑答案是否定的。)
-
如果不是,我看到的行为还有其他解释吗?
-
如果这是一个已知的问题,为什么这是一个问题,是否有更好的解决办法,我可以使用?