SQL存储过程从应用程序而不是从SSMS(Azure SQL)执行需要数小时
我们有一个存储过程,当在.NET代码中从ExecuteOnQuery调用时,它在Azure SQL server上执行需要一个多小时。当存储过程的代码通过SSM执行时,只需6.5分钟 下面是它包含的SQL代码片段(为隐私而重命名的表): TableBookProducts只有1000多万行,该联合的结果集是800万行。在SSMS中执行该语句需要6.5分钟 我们在所有表格上都有索引。ISBN13是主键(聚集),PID是非聚集索引 该语句的编译时间最长为4.9秒,看起来不像是参数嗅探。此存储过程中没有硬编码的值或参数 我试着在SSMS中将ARITHABORT设置为OFF和ON,这没有什么区别 是什么导致ExecuteOnQuery持续时间过长 服务器是否已损坏?在SQL Server上可以做些什么来提高性能SQL存储过程从应用程序而不是从SSMS(Azure SQL)执行需要数小时,sql,sql-server,azure-sql-database,Sql,Sql Server,Azure Sql Database,我们有一个存储过程,当在.NET代码中从ExecuteOnQuery调用时,它在Azure SQL server上执行需要一个多小时。当存储过程的代码通过SSM执行时,只需6.5分钟 下面是它包含的SQL代码片段(为隐私而重命名的表): TableBookProducts只有1000多万行,该联合的结果集是800万行。在SSMS中执行该语句需要6.5分钟 我们在所有表格上都有索引。ISBN13是主键(聚集),PID是非聚集索引 该语句的编译时间最长为4.9秒,看起来不像是参数嗅探。此存储过程中没
union语句中的两个查询是否应在isbn编号上合并?第一个连接在isbn编号上,第二个连接在PID上。我假设您的查询是正确的。运行以下查询以获取错误查询计划的句柄:
;WITH x AS
(
SELECT
qs.[sql_handle], qs.plan_handle,
txs = qs.statement_start_offset,
txe = qs.statement_end_offset,
[size] = cp.size_in_bytes,
[uses] = SUM(cp.usecounts),
[last] = MAX(qs.last_execution_time)
FROM
sys.dm_exec_query_stats AS qs
INNER JOIN
sys.dm_exec_cached_plans AS cp
ON qs.plan_handle = cp.plan_handle
GROUP BY
qs.[sql_handle], qs.plan_handle, cp.size_in_bytes,
qs.statement_start_offset, qs.statement_end_offset
)
SELECT
x.plan_handle,
size, uses, [last],
[statement] = COALESCE(NULLIF(
SUBSTRING(t.[text], x.txs/2,
CASE WHEN x.txe = -1 THEN 0 ELSE (x.txe - x.txs)/2 END
), ''), t.[text])
FROM x
CROSS APPLY sys.dm_exec_sql_text(x.[sql_handle]) AS t
WHERE (t.text LIKE '%UNION%' AND t.text LIKE '%CompetitorAggregation%');
然后,数据库服务器上的管理员运行以下操作:
DBCC FREEPROCCACHE (<plan_handle from above query>)
EXEC sp_recompile [<name of the stored procedure>]
IF OBJECT_ID('tempdb..#ProductAggregation') IS NOT NULL DROP TABLE [#ProductAggregation];
CREATE TABLE [#ProductAggregation](
PID decimal(12,0) NOT NULL,
ISBN13 varchar(13) NOT NULL,
CompetitorAggregationId varchar(32) NULL,
EBookWorkId int NULL,
ProductAggregationId varchar(32) NULL
PRIMARY KEY CLUSTERED (PID, ISBN13)
);
我发现了一些有趣的东西。插入临时表的非聚集索引的成本非常高。因此,我将临时表修改为以下内容:
DBCC FREEPROCCACHE (<plan_handle from above query>)
EXEC sp_recompile [<name of the stored procedure>]
IF OBJECT_ID('tempdb..#ProductAggregation') IS NOT NULL DROP TABLE [#ProductAggregation];
CREATE TABLE [#ProductAggregation](
PID decimal(12,0) NOT NULL,
ISBN13 varchar(13) NOT NULL,
CompetitorAggregationId varchar(32) NULL,
EBookWorkId int NULL,
ProductAggregationId varchar(32) NULL
PRIMARY KEY CLUSTERED (PID, ISBN13)
);
希望这能加快速度。与插入非聚集索引IX_tmpProductAggregation_PID相关的子树成本(约4000)很大
更新(3):完成上述优化后,我们的执行时间减少到8分钟
更新(4):即使进行了上述优化,我们有时也会得到长达1小时的运行时间
因此,我添加了一个与目标表的左连接,以及源(CompetitorAggregation)和目标(ProductAggregation)之间的DateChanged比较,以将数据集缩小到只包含自上次运行以来更改的记录。这将运行时间减少到1.5分钟。我们必须进行测试,看看是否会再次出现1小时的峰值或其他回归
更新(5):我也找到了摆脱工会的方法。我已将其分为两个insert语句,并更改了主键,如下所示:
PRIMARY KEY CLUSTERED (PID, ISBN13) WITH (IGNORE_DUP_KEY = ON)
这将消除查询计划中的独特排序操作。创建统计信息可能会导致此类延迟;一个执行计划可能不会触发此类统计数据的创建,而另一个执行计划则会触发。有关应用程序和SSM之间执行计划可能不同的不同原因的详细说明,请参阅(并非专门针对Azure SQL定制,但由于不同设置而导致的执行计划更改适用于所有版本的SQL Server)。我确实看过那篇文章,谢谢。这就是我想到要查看查询计划的地方。另外,我的存储过程没有参数,所以我不认为它是参数嗅探。你有异步统计数据更新吗?您是否尝试过清除计划缓存(
DBCC FREEPROCCACHE
)?为什么要使用NOLOCK
?您是否考虑过将查询重写为带有两个左连接的单个BookProducts
引用?6.5分钟可以吗?这件事看起来很糟糕,所以一个小时我都做不到。除其他建议外,您可以尝试使用UNION ALL而不是UNION,但除非您能向我们展示在每种情况下生成的执行计划,否则我们无法真正提供帮助。我将尝试DBCC FREEPROCCACHE
。问题是,这个存储过程过去需要10-15分钟,直到昨天才部署了一个小更改(甚至与此SQL语句无关)。此语句已经几个月甚至一年没有更改。如果有一个好的计划和一个坏的计划,查询存储应该能够选择/固定好的计划。