减少键查找

pet*_*ter 6 performance sql-server-2008 sql-server bookmark-lookup

我正在使用 SQL 服务器,并且我一直在仔细研究关键查找的概念,

http://blog.sqlauthority.com/2009/10/07/sql-server-query-optimization-remove-bookmark-lookup-remove-rid-lookup-remove-key-lookup/

因此,如果您有一个键查找,您可以创建一个带有“包含”列的索引,以覆盖您在 select 语句中的非索引列。

例如,

SELECT ID, FirstName FROM OneIndex WHERE City = 'Las Vegas'
GO
Run Code Online (Sandbox Code Playgroud)

该索引将包括一个键查找,

CREATE NONCLUSTERED INDEX [IX_OneIndex_City] ON [dbo].[OneIndex]
(
[City] ASC
) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

但是这个将删除密钥查找,

CREATE NONCLUSTERED INDEX [IX_OneIndex_Include] ON [dbo].[OneIndex]
(
City
) INCLUDE (FirstName,ID) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

我的意思是这对性能有多大影响?键查找的运算符成本为 0.295969 (99%),但这究竟意味着什么?

你怎么知道你在那里需要第二个索引,什么时候会变成你试图添加太多索引而不值得的情况?

在我看来,有些查询可以包括索引扫描、键查找,而且似乎仍然执行得非常快。

Pau*_*ite 13

背景

最坏的情况下,包含查找的查询必须转到物理存储,用于需要非聚集索引未涵盖的列数据的行。在最坏的最坏情况下,每次查找都需要单独的 I/O,并且执行必须等待该单行的数据价值返回才能继续。如果查找必须处理一个重要的的行数。

这就是为什么查找得到如此糟糕的新闻。另一方面,请考虑在 SQL Server 2000 中引入了查找功能。在 SQL Server 7.0 中,查询处理器只能使用非聚集索引,如果它包含所有满足查询所需的信息,;在所有其他情况下,它必须通过聚集索引(如果存在,否则通过堆扫描)访问数据。如果查找总是那么糟糕,SQL Server 肯定不会引入它们。

在 SQL Server 2000+ 中,我们有一个非聚集索引,它提供有用的排序和/或(大部分)查询所需的列,并且查找次数可能相对较少,使用非聚集索引并执行在基表上进行有限数量的查找可能是最便宜的可用访问方法(当然,尽管完全覆盖的非聚集索引可能仍然更便宜)。

在许多情况下,创建尽可能多的非聚集索引以避免扫描所有常见查询的基表是不切实际的。一个原因可能是INSERT/UPDATE/DELETE/MERGE性能比查询速度更重要(请记住,数据修改操作还必须维护所有受影响的非聚集索引)。另一个原因可能是空间;每个非聚集索引代表基表(或其上的表达式)的列子集的副本,只是排序不同。更多的数据副本意味着更多的存储空间,更多的东西在 SQL Server 的内存数据缓存中竞争空间。

其他时候,我们可以创建一些额外的索引(可能在 SQL Server 2008+ 中过滤),其中的INCLUDE列刚好满足绝大多数性能关键查询,而不会过多地影响数据修改性能,也不会使用过多的额外磁盘空间。平衡相互竞争的考虑是使索引调整更艺术而不是科学的原因。

成本

您询问查找运算符的 99% 成本在查询计划中的真正含义是什么。查询优化的成本计算部件产生一个估计该操作是总量的99%的成本估计为所述查询。数字本身(0.29)根本没有多大意义;出于所有实际目的,在比较特定查询的替代策略时,您应该将其视为优化器内部使用的无单位数字。

估算成本并未考虑您的硬件、配置、应用程序需求或其他任何因素。优化器使用的成本模型包括大量试探法和简化假设,这些假设恰好在大多数时间、对于大多数查询、在大多数硬件上产生合理的计划。这并不是说高成本运营商的计划和绩效之间没有相关性;相反,这种联系通常比通常预期的要弱得多。无论如何,首先要检查高成本计划运营商的原因,但不要将这些信息视为非常可能有缺陷的估计。

影响

我还想提几个可以改善查找影响的因素。首先,我一开始就提到最坏的情况是逐行物理 I/O。如果满足查找所需的数据页(聚集索引或堆)已经在内存(数据缓存)中,这显然可以避免。在这种情况下,具有查找和覆盖索引的计划之间的执行时间差异可能是无法估量的。即使在需要物理 I/O 的地方,如果读取次数很少,您仍然可能不在乎。(表的数据页在数据缓存中的可能性取决于许多因素,并且特定于您的硬件和环境)。

在需要更多物理 I/O 的情况下,查询计划中存在的优化仍然可以减少查找的影响。如果 SQL Server 期望查找次数很重要,它可能会选择显式排序进入嵌套循环连接的行,以按非聚集键的顺序驱动查找。这种重新排序促进了非聚集索引的顺序读取,这可能比硬件上的随机 I/O 快得多。

无论是否使用显式排序,驱动查找的嵌套循环连接都可能具有WithOrderedPrefetchWithUnorderedPrefetch属性。在任一情况下,查询执行引擎在驱动查找的索引键流中“向前看”并发出读读取。这个想法是向I/O 系统发出异步读取请求,以获取即将需要的数据页,以便在查找需要数据页时,它已经存在于内存中。

Under ideal conditions (low fragmentation, good query plan, high-performance I/O system) the read-ahead mechanism may well be fast enough to prevent even large parallel query plans from ever waiting on I/O to complete. This is especially true in Enterprise Edition, which can issue very large single I/O requests (up to 2MB per request if memory serves). On the other hand, under less than ideal (more normal!) conditions, your query may suffer horribly as it waits on long I/O queues, or fails to drive the I/O system hard enough. The worst case performance of key lookups can be very poor indeed.

Summary

In summary, you will generally want to avoid lookups where it makes sense to do so. For small queries (that are going to remain small) you may decide that the overhead of extra indexes (space & maintenance) is not justified, given due weight to the wider needs of the system and its users.

Ultimately this is all part of the art & science that is database development and administration.


Rob*_*ley 7

想象一下,电话公司有一个电话号码列表,包括客户是谁、他们住在哪里、他们的帐单号码是什么等等。主键可以是电话号码。

他们给你白页。这就像一个非聚集索引,按名称排序,包括像地址这样的列。

如果你想找到书中所有的 Farleys,并对他们的地址感兴趣,那么白页就是你所需要的。您可以快速寻找 Farleys(寻找 Fs 等),然后您就拥有了所需的所有信息。

但是,如果您想要他们的帐单号码,则需要进行查找。您可以快速找到 Farley 的所有电话号码,但随后您需要获取每个电话号码(数百个)并在主(聚集)索引中执行另一次搜索(查找),该索引按电话号码排序。其中每一个的成本与寻找 Farleys 的成本大致相同,使您的查询运行更糟。

还有一个门槛。在某个时候,数据库会意识到只需浏览聚集索引的每一页,检查每条记录以查看它是否感兴趣,会更快。

说真的 - 摆脱查找。您的查询现在可能很快,但可能无法扩展。