SQL 请求时间突然增加
SQL Request time suddenly increase
我有一个 SQL 请求,通常执行时间少于 1.5 秒。但有时,需要超过 30 秒,并且这种行为会持续数小时。
这里是实际的请求:
set dateformat ymd;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT Categories.Id as CategoryId,
(
CASE when VeryBigChildTable.Type = 9
then
Categories.Name
else
NULL
end
) as CategoryName,
OS1.Id,
OS1.StartDateUTC,
VeryBigChildTable.StartDateUTC as StartDateUTCSP,
Categories.Client_Id,
VeryBigChildTable.ArticleScriptArticleSPError_ArticleSP_Id as FailedStep,
VeryBigChildTable.[Order] as Position,
(
CASE when Categories.Id IS null
then
( cast(TSHeaders.Id as varchar(3)) + '__' + cast(OS1.Id as varchar(15) ))
else
( cast(Categories.Id as varchar(3)) + '_' + cast(OS1.Id as varchar(15) ))
end )
as GroupKey,
(
CASE when VeryBigChildTable.Type = 9
then
TSHeaders.CurrentName
else
NULL
end
) ,
SPHeaders.Id,
stuff((
SELECT '|' + ArticleCategories.Name
from ArticleCategories
inner join ArticleCategoryArticleScript
on ArticleCategoryArticleScript.BigTable_Id = OS1.Id
and ArticleCategoryArticleScript.ArticleCategories_Id = ArticleCategories.Id
for xml path('')),1,1,'')
from BigTable as OS1
inner join VeryBigChildTable on VeryBigChildTable.ArticleScript_Id = OS1.Id
inner join TSHeaders on TSHeaders.Id = OS1.TSArticle_Id
inner join SPHeaders on SPHeaders.Id = VeryBigChildTable.SPHeader_Id
inner join Categories on Categories.TSHeader_Id = OS1.TSArticle_Id
left outer join ArticleNetworks on ArticleNetworks.ArticleSPArticleNetwork_ArticleNetwork_Id = VeryBigChildTable.Id
where
OS1.StartDateUTC >= '2015-06-18 10:12:15'
and OS1.StartDateUTC <= '2015-06-19 10:12:15'
and TSHeaders.Id in (319,318,322,323,324,326,328,343,345,346,347,550,552,561,565,595,612,613)
and Categories.Id in (494,491,484,487,511,235,241,245,265,539,540,242,236,239,240,267,268,269)
本次查询returns约20K行。
VeryBigChildTable
是 2.6 亿行 table 而 BigTable
是 6000 万行 table。其他的table很小(不到2K行)。
我的配置:SQL Server 2008 R2(镜像)在 Windows 2008 Server、Xeon 16 核和 32GB RAM 上。
什么可能导致这个请求 运行 有时超过 30 秒? (知道我那段时间没有发现任何索引任务)
如何优化此请求?
欢迎大家提出意见。
可能有很多原因。
- 许多其他进程 运行 并行,您有 cpu 压力。您可以检查
is_idle
标志,例如:SELECT * FROM sys.dm_os_schedulers WHERE scheduler_id <= 256
- 您可能因其他更大的查询而遇到内存压力。您可以检查当前的内存分配
SELECT * FROM sys.dm_exec_query_memory_grants
。也许您会看到更多请求内存的查询。你也可以看看 SELECT * FROM sys.dm_os_process_memory
.
- 您可以尝试
RECOMPILE
您的查询,方法是在查询末尾使用 OPTION(RECOMPILE)
以获得适合当前情况的新查询计划。
如果这种情况时有发生,而且除了一个查询之外其他一切似乎都工作正常,我的猜测是由于某种原因该查询有一个新的查询计划。可以肯定的是,我建议检查查询计划通常是什么以及性能不好时是什么。
要在进程为 运行 时获取查询计划,您可以使用如下内容:
SELECT qp.*
FROM sys.dm_exec_requests r
cross apply sys.dm_exec_query_plan(r.plan_handle) qp
WHERE session_id = <spid of the process here>;
如果你想检查发生了什么,也想稍后得到查询计划,你可以看看dm_exec_query_stats。它还显示了不错的统计数据,如总逻辑读取、工作时间和每个语句的执行计数:
select top 100
SUBSTRING(t.text, (s.statement_start_offset/2)+1,
((CASE s.statement_end_offset
WHEN -1 THEN DATALENGTH(t.text)
ELSE s.statement_end_offset
END - s.statement_start_offset)/2) + 1) as statement_text,
t.text,
s.total_logical_reads,
s.total_logical_reads / s.execution_count as avg_logical_reads,
s.total_worker_time,
s.total_worker_time / s.execution_count as avg_worker_time,
s.execution_count,
creation_time,
last_execution_time,
cast(p.query_plan as xml) as query_plan
from sys.dm_exec_query_stats s
cross apply sys.dm_exec_sql_text (sql_handle) t
cross apply sys.dm_exec_text_query_plan (plan_handle, statement_start_offset, statement_end_offset) p
where t.text like '%VeryBigChildTable%'
-- this way you'll get everything related to the big table
order by s.total_logical_reads desc
统计信息仅适用于缓存中存在的那些计划,因此如果由于某种原因计划丢失,统计信息也会丢失。
我有一个 SQL 请求,通常执行时间少于 1.5 秒。但有时,需要超过 30 秒,并且这种行为会持续数小时。
这里是实际的请求:
set dateformat ymd;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT Categories.Id as CategoryId,
(
CASE when VeryBigChildTable.Type = 9
then
Categories.Name
else
NULL
end
) as CategoryName,
OS1.Id,
OS1.StartDateUTC,
VeryBigChildTable.StartDateUTC as StartDateUTCSP,
Categories.Client_Id,
VeryBigChildTable.ArticleScriptArticleSPError_ArticleSP_Id as FailedStep,
VeryBigChildTable.[Order] as Position,
(
CASE when Categories.Id IS null
then
( cast(TSHeaders.Id as varchar(3)) + '__' + cast(OS1.Id as varchar(15) ))
else
( cast(Categories.Id as varchar(3)) + '_' + cast(OS1.Id as varchar(15) ))
end )
as GroupKey,
(
CASE when VeryBigChildTable.Type = 9
then
TSHeaders.CurrentName
else
NULL
end
) ,
SPHeaders.Id,
stuff((
SELECT '|' + ArticleCategories.Name
from ArticleCategories
inner join ArticleCategoryArticleScript
on ArticleCategoryArticleScript.BigTable_Id = OS1.Id
and ArticleCategoryArticleScript.ArticleCategories_Id = ArticleCategories.Id
for xml path('')),1,1,'')
from BigTable as OS1
inner join VeryBigChildTable on VeryBigChildTable.ArticleScript_Id = OS1.Id
inner join TSHeaders on TSHeaders.Id = OS1.TSArticle_Id
inner join SPHeaders on SPHeaders.Id = VeryBigChildTable.SPHeader_Id
inner join Categories on Categories.TSHeader_Id = OS1.TSArticle_Id
left outer join ArticleNetworks on ArticleNetworks.ArticleSPArticleNetwork_ArticleNetwork_Id = VeryBigChildTable.Id
where
OS1.StartDateUTC >= '2015-06-18 10:12:15'
and OS1.StartDateUTC <= '2015-06-19 10:12:15'
and TSHeaders.Id in (319,318,322,323,324,326,328,343,345,346,347,550,552,561,565,595,612,613)
and Categories.Id in (494,491,484,487,511,235,241,245,265,539,540,242,236,239,240,267,268,269)
本次查询returns约20K行。
VeryBigChildTable
是 2.6 亿行 table 而 BigTable
是 6000 万行 table。其他的table很小(不到2K行)。
我的配置:SQL Server 2008 R2(镜像)在 Windows 2008 Server、Xeon 16 核和 32GB RAM 上。
什么可能导致这个请求 运行 有时超过 30 秒? (知道我那段时间没有发现任何索引任务)
如何优化此请求?
欢迎大家提出意见。
可能有很多原因。
- 许多其他进程 运行 并行,您有 cpu 压力。您可以检查
is_idle
标志,例如:SELECT * FROM sys.dm_os_schedulers WHERE scheduler_id <= 256
- 您可能因其他更大的查询而遇到内存压力。您可以检查当前的内存分配
SELECT * FROM sys.dm_exec_query_memory_grants
。也许您会看到更多请求内存的查询。你也可以看看SELECT * FROM sys.dm_os_process_memory
. - 您可以尝试
RECOMPILE
您的查询,方法是在查询末尾使用OPTION(RECOMPILE)
以获得适合当前情况的新查询计划。
如果这种情况时有发生,而且除了一个查询之外其他一切似乎都工作正常,我的猜测是由于某种原因该查询有一个新的查询计划。可以肯定的是,我建议检查查询计划通常是什么以及性能不好时是什么。
要在进程为 运行 时获取查询计划,您可以使用如下内容:
SELECT qp.*
FROM sys.dm_exec_requests r
cross apply sys.dm_exec_query_plan(r.plan_handle) qp
WHERE session_id = <spid of the process here>;
如果你想检查发生了什么,也想稍后得到查询计划,你可以看看dm_exec_query_stats。它还显示了不错的统计数据,如总逻辑读取、工作时间和每个语句的执行计数:
select top 100
SUBSTRING(t.text, (s.statement_start_offset/2)+1,
((CASE s.statement_end_offset
WHEN -1 THEN DATALENGTH(t.text)
ELSE s.statement_end_offset
END - s.statement_start_offset)/2) + 1) as statement_text,
t.text,
s.total_logical_reads,
s.total_logical_reads / s.execution_count as avg_logical_reads,
s.total_worker_time,
s.total_worker_time / s.execution_count as avg_worker_time,
s.execution_count,
creation_time,
last_execution_time,
cast(p.query_plan as xml) as query_plan
from sys.dm_exec_query_stats s
cross apply sys.dm_exec_sql_text (sql_handle) t
cross apply sys.dm_exec_text_query_plan (plan_handle, statement_start_offset, statement_end_offset) p
where t.text like '%VeryBigChildTable%'
-- this way you'll get everything related to the big table
order by s.total_logical_reads desc
统计信息仅适用于缓存中存在的那些计划,因此如果由于某种原因计划丢失,统计信息也会丢失。