SQL SERVER 索引优化——移除键查找(书签查找)或RID查找

SQL SERVER 索引优化
——移除键查找(书签查找)或RID查找

 

今天,我将分享一个快速移除键查找或RID查找的技巧。让我们首先弄明白什么是键查找或者RID查找。请注意,从SQL Server 2005 SP1 及以前的版本,键查找被称为书签查找。

当查询请求少量数据时,SQL Server 优化器将试图使用在查询结果列或包含在WHERE语句中列的非聚集索引,检索数据。如果查询请求的数据没有呈现在非聚集索引中,SQL Server 必须返回数据页去获取这些列中的数据。无论表是否有聚集索引,查询仍将到表或聚集索引中检索数据。

上面的场景中,如果表有聚集索引,其被称为键查找 (key lookup,或书签查找bookmark lookup);如果表没有聚集索引,但是有一个非聚集索引,其被称为 RID 查找。这种操作消耗很大。为优化任何包含键查找或RID查找的查询,其应该从查询计划中被移除。有两种不同的移除键查找或RID查找的方法。

在弄清楚这两种方法之前,我们将创建没有聚集索引的测试表,激发RID查找。RID 查找是使用提供的行标识符(row identifier ,RID)在堆上的书签查找。

--创建有几个列的表 OneIndex
CREATE TABLE OneIndex(ID INT,
       FirstName VARCHAR(100),
       LastName VARCHAR(100),
       City VARCHAR(100));
GO
--向表中插入10万行记录
INSERT INTO OneIndex (ID,FirstName,LastName,City)
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY a.name) RowID,
       'Bob',
       CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%2 = 1 THEN 'Smith'
       ELSE 'Brown' END,
       CASE
              WHEN ROW_NUMBER() OVER (ORDER BY a.name)%999 = 1 THEN 'Las Vegas'
              WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 1 THEN 'New York'
              WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 5 THEN 'San Marino'
              WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 3 THEN 'Los Angeles'
       ELSE 'Houston' END
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
GO

现在让我们运行下面的查询语句,并检查查询计划

因为表上没有索引,执行的是全表扫描(Table Scan)。我们将在表上创建一个聚集索引,然后再次检查执行计划。

-- 创建聚集索引
CREATE CLUSTERED INDEX [IX_OneIndex_ID] ON [dbo].[OneIndex] (
       [ID] ASC
);

现在再次运行如下查询

SELECT ID, FirstName
FROM OneIndex
WHERE City = 'Las Vegas'
GO

执行计划清晰的表明,当表上创建聚集索引后,Table Scan 现在转变为 Clustered Index Scan。在这两种情况下,基本表都是完全扫描的,表上没有搜索。

现在,让我们看一下查询的WHERE 语句。直观上来看,如果我们在表上创建包含WHERE语句中字段列的索引,可能会获得性能提升。让我们在表上创建一个非聚集索引,然后再检查执行计划。

--创建非聚集索引
CREATE NONCLUSTERED INDEX IX_OneIndex_City
ON dbo.OneIndex(city ASC);

创建非聚集索引后,我们再次执行查询,并检查执行计划

SELECT ID, FirstName
FROM OneIndex
WHERE City = 'Las Vegas'
GO

因为索引中包含WHERE语句中的字段,SQL Server 查询执行引擎使用非聚集索引从表中检索数据。然而,SELECT语句中的字段并不包含于索引中,为了获取这些列,引擎必须再次返回基础表,检索那些列。这种特定的行为被称为键查找(或书签查找)。

有两种不同的方法可以解决这类问题。我将同时展示这两种方法;然而,推荐你使用任何一种方法移除键查找。我选择方法2.

方法1:创建非聚集覆盖索引

这种方法,我们将创建同时包含SELECT 语句中的字段和WHERE语句中的字段。

CREATE NONCLUSTERED INDEX IX_OneIndex_Cover
ON dbo.OneIndex(City,Firstname,ID);

一旦上面非聚集索引被创建,其覆盖查询中的所有列,让我们运行如下查询语句,并检查我们的执行计划。

SELECT ID, FirstName
FROM OneIndex
WHERE City = 'Las Vegas'
GO

从执行计划来看,我们可以确认,键查找已经被移除,仅仅发生索引查找(index seek)。因为没有键查找,SQL Server 查询引擎没有必要再从数据页中检索数据,索引本身就包含了所有需要的数据。

方法2:创建包含列非聚集索引

这里我们将用于SELECT 语句中的字段加入到包含列中,同时WHERE语句中的字段为索引字段。在这种方法中,我们将使用SQL Server 2005 开始介绍的新语法。一个包含非键列的索引,当查询中所有列都包含在索引中,可以显著提升性能。

CREATE NONCLUSTERED INDEX IX_OneIndex_Include
ON dbo.OneIndex(City)
INCLUDE(FirstName,ID);

创建上面的索引后,再次执行查询,并查看执行计划

从执行计划可以看到,这种方法也移除了键查找。

比较方法1、方法2的逻辑读次数,发现,方法2的性能略优于方法1(方法2逻辑读6次,方法1逻辑读7次)。这也是我选择方法2的原因之一,选择方法2还有如下优点:

  • 索引可以超出索引键900字节限制

  • 索引可以包含不允许作为键列的数据类型列,如varchar(max)、nvarchar(max)或者XML

  • 主索引的大小被减小,其提升了索引操作的性能

总体来说,键查找、书签查找或RID查找降低了查询的性能,我们可以通过使用包含列索引或者覆盖索引来优化性能。

我将在另一篇文章中介绍一些与优化方法相关的概念。

如果喜欢,可以扫码关注SQL Server 公众号,将有更多精彩内容分享:

                                                                 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章