一个表扫描和一个聚集索引扫描有什么区别?

76

既然Table ScanClustered Index Scan都会扫描表中的所有记录,为什么Clustered Index Scan被认为更好呢?

举个例子,当有许多记录时,以下两者之间的性能差异是什么?

declare @temp table(
    SomeColumn varchar(50)
)

insert into @temp
select 'SomeVal'

select * from @temp

-----------------------------

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeColumn varchar(50)
)

insert into @temp
select 'SomeVal'

select * from @temp
3个回答

87
在没有聚集索引的表中(堆表),数据页不会相互链接,因此遍历页面需要进行Index Allocation Map查找
然而,聚集表中的数据页链接成双向链表,使得顺序扫描稍微快一些。当然,作为交换条件,您必须处理INSERTUPDATEDELETE中保持数据页顺序的开销。然而,堆表需要对IAM进行第二次写入。
如果您的查询具有RANGE运算符(例如:SELECT * FROM TABLE WHERE Id BETWEEN 1 AND 100),则聚集表(具有保证的顺序)将更有效率,因为它可以使用索引页面来查找相关的数据页。堆表必须扫描所有行,因为它不能依赖排序。
当然,聚集索引让您执行聚集索引搜索,这几乎是最优秀的性能... 没有索引的堆总会导致表扫描。
所以:
  • 如果你的查询选择所有行,唯一的区别是聚集索引维护的双向链表。这应该使得具有大量行的聚集表比堆表稍微快一点。

  • 对于可以(至少部分)通过聚集索引满足的WHERE子句的查询,由于排序,你将会更加优越 - 所以你不必扫描整个表。

  • 对于不能通过聚集索引满足的查询,你基本上是平等的......再次强调,唯一的区别是顺序扫描的双向链表。在任何情况下,你都是次优的。

  • 对于INSERTUPDATEDELETE,堆表可能会赢,也可能不会赢。堆表不需要维护顺序,但需要对IAM进行第二次写入。我认为相对性能差异微不足道,但也非常依赖于数据。

Microsoft有一篇whitepaper,比较了聚集索引和堆表上等效的非聚集索引(与我上面讨论的不完全相同,但接近)。他们的结论基本上是在所有表上都放置聚集索引。我将尽我所能总结他们的结果(再次注意,他们真正比较的是非聚集索引和聚集索引 - 但我认为它相对可比):

  • INSERT 性能:聚簇索引胜出约3%,因为堆需要进行第二次写操作。
  • UPDATE 性能:聚簇索引胜出约8%,因为堆需要进行第二次查找操作。
  • DELETE 性能:聚簇索引胜出约18%,因为堆需要进行第二次查找和IAM的第二次删除操作。
  • 单个 SELECT 性能:聚簇索引胜出约16%,因为堆需要进行第二次查找操作。
  • 范围 SELECT 性能:聚簇索引胜出约29%,因为堆是随机排序的。
  • 并发 INSERT:在负载下,堆表由于聚簇索引的页面分裂而胜出30%。

2
今天我也在想这个问题。感谢@Terrapin提出这个问题,也感谢@Marc给出了如此好的答案! - peakit
3
MS Exam 70461 Querying Microsoft SQL Server 2012 - Chapter 15 Lesson 1有深入的剖析。 - June
我似乎无法获得你所说的这种提升:“对于一个可以(至少部分地)由聚集索引满足的带有WHERE子句的查询,由于排序,你会更快 - 因此你不需要扫描整个表格。” 我有一个包含1000万行的表格。 SELECT Id FROM Customer WHERE Id > X 的执行时间不论我是否在Id上拥有聚集索引都是相同的。为什么?虽然我可以看到它从表扫描变成了聚集索引扫描。 - Mattias Nordqvist
2
@MattiasNordqvist - 如果你只看时间,那么你做错了。由于缓存、并发访问、CPU与磁盘时间等因素,仅通过时间来优化MS-SQL是困难的。请尝试使用SET STATISTICS IO ON来检查磁盘读取情况,这是提升性能的关键。其次,这将取决于返回的行数 - 如果百分比足够高,优化器可能会选择读取+过滤。 - Mark Brackett
@MattiasNordqvist “我有一个有1000万行的表。当我执行SELECT Id FROM Customer WHERE Id > X查询时,不论我是否在Id列上有聚集索引,它都以相同的时间执行。为什么?” 只有当聚集索引在“Id”列上时才会更快。 - IndustryUser1942

5

http://msdn.microsoft.com/en-us/library/aa216840(SQL.80).aspx

聚集索引扫描逻辑和物理运算符会扫描 Argument 列指定的聚集索引。如果有可选的 WHERE:() 谓词,只有满足谓词的行才会返回。如果 Argument 列包含 ORDERED 子句,则查询处理器已请求以聚集索引排序的顺序返回行的输出。如果不存在 ORDERED 子句,则存储引擎将以最优方式扫描索引(不能保证输出排序)。

http://msdn.microsoft.com/en-us/library/aa178416(SQL.80).aspx

表扫描逻辑和物理运算符从 Argument 列指定的表中检索所有行。如果 Argument 列中出现 WHERE:() 谓词,则仅返回满足谓词的行。


-3

表扫描必须检查表中的每一行。聚簇索引扫描只需要扫描索引,而不是表中的每条记录。这就是索引的真正作用。


10
错误的,聚集索引的叶级别就是表。 - Martin Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接