使用不同的语法表达查询有时可以帮助将您希望使用非聚集索引的信息传达给优化器。您应该在下面的表格中找到所需的计划:
SELECT
[ID],
[DeviceID],
[IsPUp],
[IsWebUp],
[IsPingUp],
[DateEntered]
FROM [dbo].[Heartbeats]
WHERE
[ID] IN
(
-- Keys
SELECT TOP (1000)
[ID]
FROM [dbo].[Heartbeats]
WHERE
[DateEntered] >= CONVERT(datetime, '2011-08-30', 121)
AND [DateEntered] < CONVERT(datetime, '2011-08-31', 121)
);
将该计划与强制非聚集索引并带有提示时产生的计划进行比较:
SELECT TOP (1000)
*
FROM [dbo].[Heartbeats] WITH (INDEX(CommonQueryIndex))
WHERE
[DateEntered] BETWEEN '2011-08-30' and '2011-08-31';
计划本质上是相同的(“键查找”无非是对聚簇索引的搜索)。两种计划形式都只会对非聚集索引执行一次查找,并且最多只能对聚集索引进行1000次查找。
重要的区别在于Top运算符的位置。定位在两个搜索之间,Top阻止优化程序用对聚簇索引的逻辑等效扫描来替换两个搜索操作。优化程序通过用等效的关系操作替换逻辑计划的某些部分来工作。Top不是关系运算符,因此重写会阻止转换为聚集索引扫描。如果优化程序能够重新定位Top运算符,则由于成本估算的工作方式,它仍然会优先选择扫描而不是搜索+查找。
搜寻的成本
在非常高的水平上,优化程序的扫描和查找成本模型非常简单:它估计320次随机查找的成本与读取1350页扫描的费用相同。这可能与任何特定的现代I / O系统的硬件功能几乎没有相似之处,但它确实可以作为实际模型很好地工作。
该模型还做出了许多简化的假设,其中主要的假设是,假定每个查询都从缓存中没有数据或索引页开始。这意味着每个I / O都会产生物理I / O,尽管实际上很少出现这种情况。即使具有冷高速缓存,预取和预读也意味着所需的页面实际上很可能在查询处理器需要它们的时候就已经存在于内存中。
另一个考虑因素是,对不在内存中的行的第一个请求将导致整个页面从磁盘中获取。随后对同一页面上的行的请求很可能不会导致物理I / O。成本核算模型确实包含逻辑,以考虑到类似这样的影响,但这并不完美。
所有这些(以及更多)意味着优化器倾向于比可能更早地切换到扫描。如果发生物理操作,则随机I / O仅比“顺序” I / O“昂贵得多”-确实访问内存中的页面非常快。即使在需要物理读取的情况下,由于碎片,扫描可能根本不会导致顺序读取,并且可以并置查找,以使该模式实质上是顺序的。加上现代I / O系统(尤其是固态)不断变化的性能特征,整个过程看起来非常不稳定。
行目标
计划中高层管理人员的出现改变了成本核算方法。优化器足够聪明,可以知道使用扫描查找1000行可能不需要扫描整个聚集索引-一旦找到1000行,它就可以停止。它在Top运算符处设置了1000行的“行目标”,并使用统计信息从那里开始进行工作,以估计行源中期望需要多少行(在这种情况下为扫描)。我在这里写了有关此计算的详细信息。
此答案中的图像是使用SQL Sentry Plan Explorer创建的。