如何避免密钥查找
How to avoid key lookup
我的数据库需要将 GUID 作为其主键,因为它与多个离线数据库同步,所以 IDENTITY 列不是一个选项,因为它会导致同步冲突。
由于 GUID 会导致高 table 碎片,我们选择向所有 tables CREATEDDATETIME 添加另一列,这是一个时间戳,并使 CREATEDDATETIME 作为 CLUSTERED 索引和 GUID 列具有已成为 NON-CLUSTERED 索引。
问题是 CREATEDDATETIME 几乎没有用在 WHERE 子句中,因此几乎所有执行计划中的查询都显示对聚簇索引 CREATEDDATETIME 的 KEY LOOKUP 以获取其数据。我想知道是否可以通过以下两种方式中的一种来提高性能:
对于所有非聚集索引,例如 GUID 列上的索引,我也
包括 CREATEDDATETIME 列?或者;
我将每个非聚集索引都作为复合键
确保聚簇索引是其中的一部分,即 GUID + CREATEDDATETIME
哪一个可能更好?
当您最终需要的信息在叶级别不可用时,就会发生键查找,因此必须转到聚簇索引来获取它。想象一下以下查询:
select a, b, c
from dbo.yourTable
where GUID = <some guid>;
如果索引中包含a、b、c列,则可以避免键查找。请注意,聚簇键自动成为每个非聚簇索引中的一个包含列(这是有道理的 - 它还能如何进行键查找?)。因此,根据实际选择的内容包含列,我认为您会看到键查找从您的查询计划中消失。
既然您在上面的注释中提到了 SQL-Azure
,可以肯定地说您将不得不测试不同的方法。您已经列出了 2 个,可能还有其他的取决于您的应用程序(数据、查询配置文件和索引覆盖范围)。
您可能已经知道,碎片对从插入中选择的影响不同。因此,您的应用需求将决定您的选择。当您针对查找进行优化时,您的插入可能会变得非常慢。
逻辑碎片和物理碎片都可能影响选项1,而选项2似乎是一个普通的开销,没有明确的优化条件(适合计划使用)。 Azure 手册中所示的计划优化技术可以提供帮助。
对于碎片化测试,我使用了不久前有人推荐的这个查询:
SELECT OBJECT_NAME (S.[object_id]) as ObjectName,
I.name as IndexName,
ROUND (S.avg_fragmentation_in_percent, 2) as FragPercent,
S.page_count as PageCount,
ROUND (S.avg_page_space_used_in_percent, 2) as PageDensity
FROM sys.dm_db_index_physical_stats
(DB_ID ('MyDatabase'), NULL, NULL, NULL, 'DETAILED') as S
CROSS APPLY sys.indexes as I
WHERE I.object_id=S.object_id
AND I.index_id=S.index_id
AND S.index_level = 0;
我的数据库需要将 GUID 作为其主键,因为它与多个离线数据库同步,所以 IDENTITY 列不是一个选项,因为它会导致同步冲突。
由于 GUID 会导致高 table 碎片,我们选择向所有 tables CREATEDDATETIME 添加另一列,这是一个时间戳,并使 CREATEDDATETIME 作为 CLUSTERED 索引和 GUID 列具有已成为 NON-CLUSTERED 索引。
问题是 CREATEDDATETIME 几乎没有用在 WHERE 子句中,因此几乎所有执行计划中的查询都显示对聚簇索引 CREATEDDATETIME 的 KEY LOOKUP 以获取其数据。我想知道是否可以通过以下两种方式中的一种来提高性能:
对于所有非聚集索引,例如 GUID 列上的索引,我也 包括 CREATEDDATETIME 列?或者;
我将每个非聚集索引都作为复合键 确保聚簇索引是其中的一部分,即 GUID + CREATEDDATETIME
哪一个可能更好?
当您最终需要的信息在叶级别不可用时,就会发生键查找,因此必须转到聚簇索引来获取它。想象一下以下查询:
select a, b, c
from dbo.yourTable
where GUID = <some guid>;
如果索引中包含a、b、c列,则可以避免键查找。请注意,聚簇键自动成为每个非聚簇索引中的一个包含列(这是有道理的 - 它还能如何进行键查找?)。因此,根据实际选择的内容包含列,我认为您会看到键查找从您的查询计划中消失。
既然您在上面的注释中提到了 SQL-Azure
,可以肯定地说您将不得不测试不同的方法。您已经列出了 2 个,可能还有其他的取决于您的应用程序(数据、查询配置文件和索引覆盖范围)。
您可能已经知道,碎片对从插入中选择的影响不同。因此,您的应用需求将决定您的选择。当您针对查找进行优化时,您的插入可能会变得非常慢。
逻辑碎片和物理碎片都可能影响选项1,而选项2似乎是一个普通的开销,没有明确的优化条件(适合计划使用)。 Azure 手册中所示的计划优化技术可以提供帮助。
对于碎片化测试,我使用了不久前有人推荐的这个查询:
SELECT OBJECT_NAME (S.[object_id]) as ObjectName,
I.name as IndexName,
ROUND (S.avg_fragmentation_in_percent, 2) as FragPercent,
S.page_count as PageCount,
ROUND (S.avg_page_space_used_in_percent, 2) as PageDensity
FROM sys.dm_db_index_physical_stats
(DB_ID ('MyDatabase'), NULL, NULL, NULL, 'DETAILED') as S
CROSS APPLY sys.indexes as I
WHERE I.object_id=S.object_id
AND I.index_id=S.index_id
AND S.index_level = 0;