本主题将重点介绍如何排查和解决哈希索引的常见问题。
搜索需要哈希索引键列的子集
问题: 哈希索引需要所有索引键列的值才能计算哈希值,并在哈希表中找到相应的行。 因此,如果查询仅包含 WHERE 子句中索引键子集的相等谓词,则 SQL Server 无法使用索引查找查找与 WHERE 子句中的谓词对应的行。
相比之下,排序索引(如基于磁盘的非聚集索引和内存优化的非聚集索引)支持索引查找索引键列的子集,只要它们是索引中的前导列。
症状: 这会导致性能下降,因为 SQL Server 需要执行完整表扫描,而不是索引查找,这通常是更快的作。
如何进行故障排除:除了性能下降之外,对查询计划的检查还会显示有扫描而非索引查找。 如果查询相当简单,则查询文本和索引定义的检查也会显示搜索是否需要索引键列的子集。
请考虑下表和查询:
CREATE TABLE [dbo].[od]
(
o_id INT NOT NULL,
od_id INT NOT NULL,
p_id INT NOT NULL,
CONSTRAINT PK_od PRIMARY KEY NONCLUSTERED HASH (o_id, od_id) WITH (BUCKET_COUNT = 10000)
)
WITH (MEMORY_OPTIMIZED = ON)
SELECT p_id
FROM dbo.od
WHERE o_id=1
该表对两列(o_id、od_id)具有哈希索引,而查询对 (o_id) 具有相等谓词。 由于查询仅对索引键列的子集具有相等谓词,因此 SQL Server 无法使用 PK_od 执行索引查找作;相反,SQL Server 必须还原为完整索引扫描。
解决方法: 有多种可能的解决方法。 例如:
将索引重新创建为非聚集类型,而不是非聚集哈希。 内存优化的非聚集索引是有序的,因此 SQL Server 可以对前导索引键列执行索引查找。 本示例生成的主键定义为
constraint PK_od primary key nonclustered。更改当前索引键以匹配 WHERE 子句中的列。
添加与查询 WHERE 子句中的列匹配的新哈希索引。 在此示例中,生成的表定义将如下所示:
CREATE TABLE dbo.od ( o_id INT NOT NULL, od_id INT NOT NULL, p_id INT NOT NULL, CONSTRAINT PK_od PRIMARY KEY NONCLUSTERED HASH (o_id,od_id) WITH (BUCKET_COUNT=10000), INDEX ix_o_id NONCLUSTERED HASH (o_id) WITH (BUCKET_COUNT=10000) ) WITH (MEMORY_OPTIMIZED=ON)
请注意,如果给定索引键值存在大量重复行,内存优化哈希索引不会以最佳方式执行:在此示例中,如果 o_id列的唯一值数比表中的行数小得多,则添加索引不是最佳方法(o_id):相反,将索引类型从哈希更改为非聚集索引PK_od将是更好的解决方案。 有关详细信息,请参阅 确定哈希索引的正确桶数。