这是我的故事:我有一个长时间运行的查询,执行大约需要10秒。为了优化它,我创建了一个我认为可能有帮助的索引 - sys.dm_db_missing_index_group_stats
系统视图也推荐了它的创建。
但是服务器决定不使用索引。出于好奇,我试图通过提示强制它 - 并且查询以0.5秒完成!
我试图找出服务器忽略索引的原因。我更新了查询中包含的所有表的统计信息,并使用fullscan来确定。但没有变化。
然后我发现了令人惊讶的事情:根据执行计划,原始查询(执行10s)的估计子树成本是0.675但是使用强制索引(执行0.5s)的查询的估计子树成本是3.28 - 请参阅包含执行计划的详细信息
如果我理解得很好,服务器会认为带有索引的执行计划比没有它的计划运行得慢得多,但实际情况则相反。差异非常大。怎么可能?
为确保实际执行时间,我已将STATISTICS TIME设置为开启并获得以下结果:
原始查询
SQL Server Execution Times:
CPU time = 10766 ms, elapsed time = 11046 ms.
使用强制索引查询:
SQL Server Execution Times:
CPU time = 203 ms, elapsed time = 433 ms.
我已经谷歌搜索了一段时间但没有发现任何线索。我不认为并行性可以给出任何解释(参见Measuring Query Performance : "Execution Plan Query Cost" vs "Time Taken"),因为两个查询都以并行度= 1运行,并且"快速"查询不仅耗时短,而且CPU时间也很短。
我的环境:Windows Server 2012 R2 64位,SQL Server 2012(v11.0.5613.0),64GB RAM,16核。最大的表t_object
有大约2500万行。
以下是批量运行的两个查询的执行计划,首先是原始查询,第二个是强制索引的查询:
这是"框架部分" XML中的执行计划。在我发现如何不超过体型限制后,我可以提供一个完整的:
<?xml version="1.0" encoding="utf-16"?>
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementCompId="3" StatementEstRows="241" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.675167" StatementText="SELECT TOP 241 t_Object.c_id AS id, t_Object.c_objectDefName AS objectDefName, t_Object.c_id AS display, t_Object.c_owner AS owner, t_Object.c_ownerDefName AS ownerDefName 
, t_KPDokBase.c_typ, a_KPCisTypDokumentu0.c_nazev, t_KPDokBase.c_kpid, t_KPDokBase.c_pojistnik, t_KPDokBase.c_kpdatum, t_KPDokBase.c_urgentni, t_KPDokBase.c_poznamka
 FROM t_KPDokBase LEFT JOIN t_KPCiselnik a_KPCisTypDokumentu0 ON t_KPDokBase.c_typ=a_KPCisTypDokumentu0.c_Id , t_Document, t_Object 
 WHERE t_Document.c_Id = t_KPDokBase.c_Id AND t_Object.c_Id = t_Document.c_Id AND ((t_Object.c_createDT >= '20151021 10:16:04.875') AND (((t_KPDokBase.c_pravaTyp = '') OR (t_KPDokBase.c_pravaTyp <> 'zdravotni')))) AND t_Document.c_trashed=0 AND t_Document.c_versionType=1
 --doba využití procesoru = 10531 ms, uplynulá doba = 10816 ms.

" StatementType="SELECT" QueryHash="0x5B65E1C5AEEE1DB3" QueryPlanHash="0x3AEC766C66B98CA1" RetrievedFromCache="true">
<QueryPlan DegreeOfParallelism="1" MemoryGrant="1376" CachedPlanSize="72" CompileTime="57" CompileCPU="57" CompileMemory="848">
<MemoryGrantInfo SerialRequiredMemory="1024" SerialDesiredMemory="1376" RequiredMemory="1024" DesiredMemory="1376" RequestedMemory="1376" GrantWaitTime="0" GrantedMemory="1376" MaxUsedMemory="640" />
<OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="209569" EstimatedPagesCached="419139" EstimatedAvailableDegreeOfParallelism="8" />
<RelOp AvgRowSize="271" EstimateCPU="2.41E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimatedExecutionMode="Row" EstimateRows="241" LogicalOp="Top" NodeId="0" Parallel="false" PhysicalOp="Top" EstimatedTotalSubtreeCost="0.675167">
<OutputList>
</OutputList>
<RunTimeInformation>
<RunTimeCountersPerThread Thread="0" ActualRows="241" ActualEndOfScans="1" ActualExecutions="1" />
</RunTimeInformation>
<Top RowCount="false" IsPercent="false" WithTies="false">
<TopExpression>
<ScalarOperator ScalarString="(241)">
<Const ConstValue="(241)" />
</ScalarOperator>
</TopExpression>
</Top>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
<Batch>
<Statements>
<StmtSimple StatementCompId="4" StatementEstRows="241" StatementId="2" StatementOptmLevel="FULL" StatementSubTreeCost="3.28305" StatementText="SELECT TOP 241 t_Object.c_id AS id, t_Object.c_objectDefName AS objectDefName, t_Object.c_id AS display, t_Object.c_owner AS owner, t_Object.c_ownerDefName AS ownerDefName 
, t_KPDokBase.c_typ, a_KPCisTypDokumentu0.c_nazev, t_KPDokBase.c_kpid, t_KPDokBase.c_pojistnik, t_KPDokBase.c_kpdatum, t_KPDokBase.c_urgentni, t_KPDokBase.c_poznamka
 FROM t_KPDokBase LEFT JOIN t_KPCiselnik a_KPCisTypDokumentu0 ON t_KPDokBase.c_typ=a_KPCisTypDokumentu0.c_Id , t_Document, t_Object WITH (INDEX(IX_cdt_obdn_ow_owdn))
 WHERE t_Document.c_Id = t_KPDokBase.c_Id AND t_Object.c_Id = t_Document.c_Id AND ((t_Object.c_createDT <= '20151021 10:16:04.875') AND (((t_KPDokBase.c_pravaTyp = '') OR (t_KPDokBase.c_pravaTyp <> 'zdravotni')))) AND t_Document.c_trashed=0 AND t_Document.c_versionType=1
 --doba využití procesoru = 218 ms, uplynulá doba = 669 ms.
" StatementType="SELECT" QueryHash="0xED6590A88AF84552" QueryPlanHash="0x61ED516F58A6CB68" RetrievedFromCache="true">
<StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
<QueryPlan DegreeOfParallelism="1" MemoryGrant="2451104" CachedPlanSize="80" CompileTime="26" CompileCPU="26" CompileMemory="800">
<MemoryGrantInfo SerialRequiredMemory="1536" SerialDesiredMemory="2451104" RequiredMemory="1536" DesiredMemory="2451104" RequestedMemory="2451104" GrantWaitTime="0" GrantedMemory="2451104" MaxUsedMemory="2632" />
<OptimizerHardwareDependentProperties EstimatedAvailableMemoryGrant="209569" EstimatedPagesCached="419139" EstimatedAvailableDegreeOfParallelism="8" />
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>