SQL Server 执行计划和索引删除

Min*_*ick 3 index sql-server execution-plan

当 SQL Server 索引被删除(假设它是重复的)时,引擎采取哪些步骤来清除并重建引用它的执行计划?特别是我想知道它是一个一次性的过程,一个新的查询发生过程还是介于两者之间的过程。谢谢。

J.D*_*.D. 5

欢迎来到 DBA.StackExchange,还有一个有趣的问题!...一个我以前从未想过的问题。我不得不仔细检查我自己认为这个过程是什么,所以我认为这个StackOverflow Answer应该是你正在寻找的。特别是答案的第一个陈述:

删除/重建索引将导致使用此表/索引视图的任何缓存执行计划无效。并且 sql server 会在下次执行时生成一个新的执行计划。

本质上,计划缓存中的计划将自动标记为对引用已删除索引的任何现有执行计划无效。然后,下次运行涉及删除索引的实体的查询时,将自动生成新的执行计划并将其缓存在计划缓存中,因为旧的已失效。(最终,无效的计划将从计划缓存中清除,以便为缓存的新计划腾出空间。)

这是一个额外的资源,它同意这是如何发生的:删除不必要的索引 - 对 SQL Server 2005 中的查询计划的影响

  • 谢谢JD。那确实完全回答了我的问题。 (2认同)

Tib*_*szi 5

更详细地讲,删除索引时不会刷新计划。请参阅下面的重现。

该计划保留在缓存中,但现在无效。下次该计划可供使用时,SQL Server 会检查它是否有效。例如,如果进行了架构更改(如删除索引),则它无效。所以会产生一个新的计划。

在删除索引时删除计划将需要 AFAIK 不可用的元数据。SQL Server 必须为每个计划缓存提供关于它们依赖哪些对象的内存元数据。或者必须针对每个元数据更改(如 DROP INDEX)搜索计划,以查看哪些计划可能无效。实用的方法是在使用时检查计划是否必须重新编译,这是 SQL Server 方法(至少根据我下面的重现)。

我记得大约 3 年前,当 sp_recompile 被引入到产品中时。在此之前,我们编写了自己的,我们只是撞了 sysobjects 系统表中的 schema_ver 列(并处理溢出,因为它是一个 tinyint)。然后是 sp_recompile 的产品正是这样做的。

前进 3 年,产品已经进化,包括一次完全重写(6.5 到 7.0 之间)和另一次完全重写元数据(2000 年到 2005 年),但显然相同的基本原则已经到位。

请注意, sp_recompile 似乎确实从缓存中主动刷新对象(如果您阅读源代码)。不过,它似乎不会发生在表上(参见演示),但可能发生在代码模块上。

USE Adventureworks

DROP INDEX IF EXISTS x ON Sales.SalesOrderDetail

CREATE INDEX x ON Sales.SalesOrderDetail(OrderQty)
DBCC FREEPROCCACHE
GO

SELECT SUM(UnitPrice) FROM Sales.SalesOrderDetail WHERE OrderQty = 34
GO

SELECT qs.creation_time, sql.text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text (qs.plan_handle) AS sql
WHERE sql.text LIKE '
SELECT SUM(UnitPrice) FROM Sales.SalesOrderDetail%'
GO

WAITFOR DELAY '00:00:00.5'

DROP INDEX IF EXISTS x ON Sales.SalesOrderDetail
GO
WAITFOR DELAY '00:00:00.5'

SELECT qs.creation_time, sql.text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text (qs.plan_handle) AS sql
WHERE sql.text LIKE '
SELECT SUM(UnitPrice) FROM Sales.SalesOrderDetail%'
GO
--Note same creation_time for the plan for the two above SELECTS. The same.

--Below will produce a new plan, though
GO

SELECT SUM(UnitPrice) FROM Sales.SalesOrderDetail WHERE OrderQty = 34
GO

SELECT qs.creation_time, sql.text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text (qs.plan_handle) AS sql
WHERE sql.text LIKE '
SELECT SUM(UnitPrice) FROM Sales.SalesOrderDetail%'
GO

EXEC sp_recompile 'Sales.SalesOrderDetail'
GO

SELECT qs.creation_time, sql.text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text (qs.plan_handle) AS sql
WHERE sql.text LIKE '
SELECT SUM(UnitPrice) FROM Sales.SalesOrderDetail%'
GO
--sp_recompile doesn't avticely remove plan from cache, at least not for a table
Run Code Online (Sandbox Code Playgroud)