非常慢的存储过程
Posted
技术标签:
【中文标题】非常慢的存储过程【英文标题】:Very slow stored procedure 【发布时间】:2011-06-30 23:58:28 【问题描述】:我在查询优化方面遇到了困难,目前我非常接近重新设计数据库的点。而***是我最后的希望。我认为仅向您显示查询还不够,因此我不仅链接了数据库脚本,还链接了数据库备份,以防您不想手动生成数据
Here你可以找到脚本和备份
当您尝试执行以下操作时,问题就开始了......
exec LockBranches @count=64,@lockedBy='034C0396-5C34-4DDA-8AD5-7E43B373AE5A',@lockedOn='2011-07-01 01:29:43.863',@unlockOn='2011-07-01 01:32:43.863'
这部分出现的主要问题:
UPDATE B
SET B.LockedBy = @lockedBy,
B.LockedOn = @lockedOn,
B.UnlockOn = @unlockOn,
B.Complete = 1
FROM
(
SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete
FROM Objectives AS O
INNER JOIN Generations AS G ON G.ObjectiveID = O.ID
INNER JOIN Branches AS B ON B.GenerationID = G.ID
INNER JOIN
(
SELECT SB.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes
FROM SpicieBranches AS SB
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
INNER JOIN
(
SELECT P.ID, 1 AS SuitableProbes
FROM Probes AS P
/* ----> */ INNER JOIN Results AS R ON P.ID = R.ProbeID /* SSMS Estimated execution plan says this operation is the roughest */
GROUP BY P.ID
HAVING COUNT(R.ID) > 0
) AS X ON P.ID = X.ID
GROUP BY SB.BranchID
) AS X ON X.BranchID = B.ID
WHERE
(O.Active = 1)
AND (B.Sealed = 0)
AND (B.GenerationNo < O.BranchGenerations)
AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0)
AND (B.Complete = 1 OR X.SuitableProbes = O.BranchSize * O.EstimateCount * O.ProbeCount)
) AS B
编辑:这是每个表中的行数:
Spicies 71536
Results 10240
Probes 10240
SpicieBranches 4096
Branches 256
Estimates 5
Generations 1
Versions 1
Objectives 1
【问题讨论】:
已经尝试恢复,但没有 R2 unfort。表Results
和Probes
分别有多少行?
我已编辑问题以显示行数
SqlServer Profiler 为 CPU/Reads/Writes/Duration 提供大约 6300/500000/670/8100
【参考方案1】:
其他人可能比我能更好地解释为什么这要快得多。经验告诉我,当您有一堆查询一起运行很慢但在各个部分应该很快时,那么值得尝试使用临时表。
这样更快
ALTER PROCEDURE LockBranches
-- Add the parameters for the stored procedure here
@count INT,
@lockedOn DATETIME,
@unlockOn DATETIME,
@lockedBy UNIQUEIDENTIFIER
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON
--Create Temp Table
SELECT SpicieBranches.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes
INTO #BranchSuitableProbeCount
FROM SpicieBranches
INNER JOIN Probes AS P ON P.SpicieID = SpicieBranches.SpicieID
INNER JOIN
(
SELECT P.ID, 1 AS SuitableProbes
FROM Probes AS P
INNER JOIN Results AS R ON P.ID = R.ProbeID
GROUP BY P.ID
HAVING COUNT(R.ID) > 0
) AS X ON P.ID = X.ID
GROUP BY SpicieBranches.BranchID
UPDATE B SET
B.LockedBy = @lockedBy,
B.LockedOn = @lockedOn,
B.UnlockOn = @unlockOn,
B.Complete = 1
FROM
(
SELECT TOP (@count) Branches.LockedBy, Branches.LockedOn, Branches.UnlockOn, Branches.Complete
FROM Objectives
INNER JOIN Generations ON Generations.ObjectiveID = Objectives.ID
INNER JOIN Branches ON Branches.GenerationID = Generations.ID
INNER JOIN #BranchSuitableProbeCount ON Branches.ID = #BranchSuitableProbeCount.BranchID
WHERE
(Objectives.Active = 1)
AND (Branches.Sealed = 0)
AND (Branches.GenerationNo < Objectives.BranchGenerations)
AND (Branches.LockedBy IS NULL OR DATEDIFF(SECOND, Branches.UnlockOn, GETDATE()) > 0)
AND (Branches.Complete = 1 OR #BranchSuitableProbeCount.SuitableProbes = Objectives.BranchSize * Objectives.EstimateCount * Objectives.ProbeCount)
) AS B
END
平均执行时间为 54 毫秒,而原始执行时间为 6 秒,这要快得多。
编辑
看了一下,并将我的想法与 RBarryYoung 解决方案中的想法结合起来。如果你使用以下创建临时表
SELECT SB.BranchID AS BranchID, COUNT(*) AS SuitableProbes
INTO #BranchSuitableProbeCount
FROM SpicieBranches AS SB
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
WHERE EXISTS(SELECT * FROM Results AS R WHERE R.ProbeID = P.ID)
GROUP BY SB.BranchID
然后您可以将其缩短到 15 毫秒,这比我们开始时要快 400 倍。查看执行计划表明临时表上发生了表扫描。通常您会尽可能避免表扫描,但对于 128 行(在这种情况下),它比以前所做的更快。
【讨论】:
请原谅我滥用你的命名约定我发现当一切都被别名时,SQL 真的很难阅读。 嗨大卫,这真的很奇怪,它的运行速度真的非常快,我不明白它怎么会发生......关于约定不是问题,如果你愿意,可以使用你自己的.. . 也通过我的解决方案:),连接不是 100% ,它只是到所需的扩展。从而影响执行计划中的表扫描和其他...【参考方案2】:这基本上是一个完整的猜测,但过去我发现加入子查询的结果可能非常慢。也就是说,子查询在确实不需要时被评估了太多次。 解决这个问题的方法是将子查询移动到 CTE 中,然后加入这些 CTE。祝你好运!
【讨论】:
【参考方案3】:两个uniqueidentifier
列上的连接似乎是问题的根源。一个是聚集索引,另一个是非聚集索引(FK表)。很好,它们上有索引。不幸的是,在加入大量行时,guid 的性能是出了名的差。
作为故障排除步骤:
索引处于什么状态?上次更新统计信息是什么时候? 当临时执行时,该子查询对其自身的性能如何?即当您自己运行此语句时,结果集返回的速度有多快?可以接受吗? 重建 2 个索引并更新统计信息后,是否有任何可衡量的差异?SELECT P.ID, 1 AS SuitableProbes FROM Probes AS P
INNER JOIN Results AS R ON P.ID = R.ProbeID
GROUP BY P.ID HAVING COUNT(R.ID) > 0
【讨论】:
所有索引都被重建和重组。每一个都有碎片 【参考方案4】:以下在我的系统上运行速度提高了大约 15 倍:
UPDATE B
SET B.LockedBy = @lockedBy,
B.LockedOn = @lockedOn,
B.UnlockOn = @unlockOn,
B.Complete = 1
FROM
(
SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete
FROM Objectives AS O
INNER JOIN Generations AS G ON G.ObjectiveID = O.ID
INNER JOIN Branches AS B ON B.GenerationID = G.ID
INNER JOIN
(
SELECT SB.BranchID AS BranchID, COUNT(*) AS SuitableProbes
FROM SpicieBranches AS SB
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
WHERE EXISTS(SELECT * FROM Results AS R WHERE R.ProbeID = P.ID)
GROUP BY SB.BranchID
) AS X ON X.BranchID = B.ID
WHERE
(O.Active = 1)
AND (B.Sealed = 0)
AND (B.GenerationNo < O.BranchGenerations)
AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0)
AND (B.Complete = 1 OR X.SuitableProbes = O.BranchSize * O.EstimateCount * O.ProbeCount)
) AS B
【讨论】:
【参考方案5】:子查询插入本地临时表
SELECT SB.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes
into #temp FROM SpicieBranches AS SB
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
INNER JOIN
(
SELECT P.ID, 1 AS SuitableProbes
FROM Probes AS P
/* ----> */ INNER JOIN Results AS R ON P.ID = R.ProbeID /* SSMS Estimated execution plan says this operation is the roughest */
GROUP BY P.ID
HAVING COUNT(R.ID) > 0
) AS X ON P.ID = X.ID
GROUP BY SB.BranchID
下面的查询显示了与对应表的部分连接,而不是完整的!!
UPDATE B
SET B.LockedBy = @lockedBy,
B.LockedOn = @lockedOn,
B.UnlockOn = @unlockOn,
B.Complete = 1
FROM
(
SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete
From
(
SELECT ID, BranchGenerations, (BranchSize * EstimateCount * ProbeCount) as MultipliedFactor
FROM Objectives AS O WHERE (O.Active = 1)
)O
INNER JOIN Generations AS G ON G.ObjectiveID = O.ID
Inner Join
(
Select Sealed, GenerationNo, LockedBy, UnlockOn, ID, Complete
From Branches
Where B.Sealed = 0 AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0)
)B ON B.GenerationID = G.ID
INNER JOIN
(
Select * from #temp
) AS X ON X.BranchID = B.ID
WHERE
AND (B.GenerationNo < O.BranchGenerations)
AND (B.Complete = 1 OR X.SuitableProbes = O.MultipliedFactor)
) AS B
【讨论】:
除了一些语法差异之外,这与我上面给出的解决方案相同。很高兴看到我们的想法是一样的。 我重复一遍,在我的情况下,连接不是 100% ,它只是到所需的扩展。以上是关于非常慢的存储过程的主要内容,如果未能解决你的问题,请参考以下文章