聚簇索引:SQL Server索引级别3

本文是“Stairway系列:SQL Server索引的阶梯”的一部分

索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图。不幸的是,当性能问题出现时,索引往往被添加为事后考虑。这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员“快速”

这个阶段的前面的层次提供了一般索引和非聚集索引的概述。它以下面关于SQL Server索引的关键概念结束。当请求到达您的数据库时,无论是SELECT语句还是INSERT,UPDATE或DELETE语句,SQL Server都只有三种可能的方式来访问语句中引用的表的数据:

?只访问非聚集索引并避免访问表。这只能在索引包含查询请求的这个表的所有数据时才有可能

?使用搜索键访问索引,然后使用选定的书签访问表中的单个行。

?忽略索引并在表中搜索请求的行。

这个级别的重点是上面列表中的第三个选项。搜索桌子。这反过来又会引导我们讨论聚集索引。在第二级提到但没有涉及的主题。

我们将在此级别使用的主要AdventureWorks数据库表是SalesOrderDetail表。在121,317行,它足以说明在表上有聚集索引的一些好处。而且,有两个外键,足以说明一些关于聚簇索引的设计决策。

示例数据库

尽管我们已经讨论过一级的样本数据库,但是这个时候还是要重复的。 在整个这个阶段,我们将用例子来说明概念。 这些示例基于Microsoft AdventureWorks示例数据库。 我们专注于销售订单。 五张表将给我们交易和非交易数据的良好组合; Customer,SalesPerson,Product,SalesOrderHeader和SalesOrderDetail。 为了保持重点,我们使用列的一个子集。 由于AdventureWorks规范化很好,销售人员信息被分解为三个表:SalesPerson,Employee和Contact。

在整个阶梯中,我们使用以下两个术语来交换订单上的单行:“订单项”和“订单明细”。 前者是更常见的业务术语; 后者出现在AdventureWorks表的名字内。

图1显示了一整套表格及其之间的关系。

图1:这个Stairway的例子中使用的表

注意:

在这个楼梯级别显示的所有TSQL代码可以与文章一起下载。

聚集索引

我们首先提出以下问题:如果不使用非聚集索引,需要多少工作才能在表中找到一行?在表中搜索请求的行意味着扫描无序表中的每一行吗?或者,SQL Server可以永久性地对表中的行进行排序,以便通过搜索关键字快速访问它们,就像通过搜索关键字快速访问非聚集索引的条目一样?答案取决于您是否指示SQL Server在表上创建聚簇索引。

与非聚簇索引是一个独立的对象并占用他们自己的空间不同,聚簇索引和表是一样的。通过创建聚集索引,可以指示SQL Server将表中的行排序为索引键序列,并在将来的数据修改期间维护该序列。即将到来的级别将查看生成的内部数据结构来完成此操作。但现在,把聚簇索引看作是一个有序表。给定一个行的索引键值,SQL Server可以快速访问该行;并可以从该行按顺序进行。

为了演示目的,我们创建了示例表SalesOrderDetail的两个副本;一个没有索引,一个有聚集索引。关于索引的关键字段,我们做出与AdventureWorks数据库的设计者做出相同的选择:SalesOrderID / SalesOrderDetailID。清单1中的代码创建了SalesOrderDetail表的副本。我们可以随时重新运行这个代码,我们希望从一个“干净的石板”开始。

IF EXISTS (SELECT * FROM sys.tables 

WHERE OBJECT_ID = OBJECT_ID(‘dbo.SalesOrderDetail_index‘))

DROP TABLE dbo.SalesOrderDetail_index;

GO

IF EXISTS (SELECT * FROM sys.tables 

WHERE OBJECT_ID = OBJECT_ID(‘dbo.SalesOrderDetail_noindex‘))

DROP TABLE dbo.SalesOrderDetail_noindex;

GO

SELECT * INTO dbo.SalesOrderDetail_index FROM Sales.SalesOrderDetail;

SELECT * INTO dbo.SalesOrderDetail_noindex FROM Sales.SalesOrderDetail;

GO

CREATE CLUSTERED INDEX IX_SalesOrderDetail

ON dbo.SalesOrderDetail_index (SalesOrderID, SalesOrderDetailID)

GO

清单1:创建SalesOrderDetail表的副本

因此,在创建聚集索引之前,假设SalesOrderDetail表如下所示:

SalesOrderID SalesOrderDetailID ProductID   OrderQty UnitPrice
69389    102201       864      3     38.10
56658    59519       711      1     34.99
59044    70000       956      2     1430.442
48299    22652       853      4     44.994
50218    31427       854      8     44.994
53713    50716        711      1     34.99
50299    32777       739      1     744.2727
45321    6303        775      6     2024.994
72644    115325       873      1     2.29
48306    22705       824      4     141.615
69134    101554       876      1     120.00
48361    23556        760      3     469.794
53605    50098       888      1     602.346
48317     22901        722      1     183.9382
66430    93291        872      1      8.99
65281    90265       889      2     602.346
52248    43812       871      1     9.99
47978    20189       794      2     1308.9375

在创建如上所示的聚集索引之后,生成的表/聚集索引将如下所示:

SalesOrderID SalesOrderDetailID ProductID   OrderQty UnitPrice
43668     106        722      3    178.58
43668     107        708      1    20.19
43668     108        733      3     356.90
43668     109        763      3    419.46
43669     110        747      1    714.70
43670     111        710      1    5.70
43670     112        709      2    5.70
43670     113        773      2    2,039.99
43670     114        776      1    2,024.99
43671     115        753       1    2,146.96
43671     116        714      2    28.84
43671     117        756      1    874.79
43671     118        768      2    419.46
43671     119        732      2    356.90
43671     120        763      2    419.46
43671     121        755      2    874.79
43671     122        764      2    419.46
43671     123        716      1    28.84
43671     124        711      1    20.19
43671      125        708          1     20.19
43672     126        709      6     5.70
43672     127        776      2    2,024.99
43672     128        774      1    2,039.99
43673     129        754      1    874.79
43673     130        715       3    28.84
43673     131        729      1    183.94

当您查看上面显示的示例数据时,您可能会注意到每个SalesOrderDetailID值都是唯一的。不要混淆; SalesOrderDetailID不是表的主键。 SalesOrderID / SalesOrderDetailID的组合是表的主键;以及聚簇索引的索引键。

了解群集索引的基础知识

聚簇索引键可以由您选择的任何列组成;它不必以主键为基础。在我们的例子中,最重要的是最左边的一列是一个外键,即SalesOrderID值。因此,销售订单的所有行项目都会在SalesOrderDetail表中连续出现。

请记住以下有关SQL Server聚簇索引的附加要点:

?由于聚簇索引的条目是表的行,聚簇索引条目中没有书签值。当SQL Server已经在一行时,它不需要一条信息告诉它在哪里找到那一行。

?聚簇索引始终覆盖查询。由于索引和表是一样的,表的每一列都在索引中。

?在表上创建聚簇索引不会影响在该表上创建非聚簇索引的选项。

选择聚集索引键列

每个表最多可以有一个聚簇索引。表格的行只能是一个序列。你需要决定什么样的顺序,如果有的话,对每个表最好;并在可能的情况下在表格填充数据之前创建聚集索引。在做出这个决定时,要记住排序不仅意味着排序,而且意味着分组;如按销售订单对订单项进行分组。

这就是为什么AdventureWorks数据库的设计者选择SalesOrderID内的SalesOrderDetailID作为SalesOrderDetail表的顺序的原因;这是订单项的自然顺序。

例如,如果用户请求订单的订单项,则通常会请求该订单的所有订单项。一个典型的销售订单表单告诉我们,订单的印刷版本总是包含所有的行项目。销售订单业务的性质是按销售订单对行项目进行分组。仓库偶尔会要求按产品而不是销售订单查看订单项,但大部分的要求;如销售人员或客户,打印发票的程序或计算每个订单总价值的查询;将需要所有销售订单的所有行项目。

然而,用户需求本身并不能决定什么是最好的聚集索引。本系列的未来级别将覆盖指标的内部;因为索引的某些内部方面也会影响你对聚簇索引列的选择。

如果表中没有聚集索引,则该表称为堆。每个表都是一个堆或一个聚集索引。所以,虽然我们经常说每一个指标都属于聚类或非聚类两种类型之一,同样重要的是要注意,每张桌子都属于两种类型之一;它是一个聚集索引或它是一堆。开发人员经常说,一个表“有”或“没有”聚集索引,但更有意义的说,表“是”或“不是”聚集索引。

SQL Server在查找行(不包括使用非聚簇索引)时搜索堆只有一种方法,这是从表的第一行开始,然后继续执行表,直到读取所有行。没有序列,没有搜索键,也无法快速导航到特定的行。

比较聚簇索引和堆

为了评估聚簇索引与堆的性能,清单1创建了SalesOrderDetailtable的两个副本。一个副本是堆版本,另一个是创建原始表(SalesOrderID,SalesOrderDetailID)上的同一个聚集索引。这两个表都没有任何非聚集索引。

我们将对每个版本的表执行相同的三个查询;一个检索单个行,一个检索单个订单的所有行,一个检索单个产品的所有行。我们在下面的表格中给出了SQL和每个执行的结果。

我们的第一个查询检索单个行,执行细节显示在表1中。


SQL


SELECT *
FROM SalesOrderDetail
WHERE SalesOrderID = 43671
AND SalesOrderDetailID = 120


Heap


(1 row(s) affected)
Table ‘SalesOrderDetail_noindex‘. Scan count 1, logical reads 1495.


Clustered Index


(1 row(s) affected)
Table ‘SalesOrderDetail_noindex‘. Scan count 1, logical reads 3.


Impact of having the Clustered Index


IO reduced from 1495 reads to 3 reads.


Comments


No surprise.  Table scanning 121,317 rows to find just one is not very efficient.

表1:检索单行

我们的第二个查询检索单个销售订单的所有行,您可以在表2中看到执行的详细信息。


SQL


SELECT *
FROM SalesOrderDetail
WHERE SalesOrderID = 43671


Heap


(11 row(s) affected)
Table ‘SalesOrderDetail_noindex‘. Scan count 1, logical reads 1495.


Clustered Index


(11 row(s) affected)
Table ‘SalesOrderDetail_noindex‘. Scan count 1, logical reads 3.


Impact of having the Clustered Index


IO reduced from 1495 reads to 3 reads.


Comments


Same statistics as the previous query.  The heap still required a table scan, while the clustered index grouped the 11 detail rows of the requested order sufficiently close together so that the IO required to retrieve 11 rows was the same as the IO required to retrieve one row.  An upcoming Level will explain in detail why no additional reads were required to retrieve the additional 10 rows.

表2:检索单个SalesOrder的所有行

我们的第三个查询检索单个产品的所有行,执行结果如表3所示。


SQL


SELECT *
FROM SalesOrderDetail
WHERE ProductID = 755


Heap


(228 row(s) affected)
Table ‘SalesOrderDetail_noindex‘. Scan count 1, logical reads 1495.


Clustered Index


(228 row(s) affected)
Table ‘SalesOrderDetail_index‘. Scan count 1, logical reads 1513.


Impact of having the Clustered Index


IO slightly greater for the clustered index version; 1513 reads versus 1495 reads.


Comments


Without a nonclustered index on the ProductID column to help find the rows for a single Product, both versions had to be scanned.  Because of the overhead of having a clustered index, the clustered index version is the slightly larger table; therefore scanning it required a few more reads than scanning the heap.

表3:检索单个产品的所有行

前两个查询大大受益于聚簇索引的存在;第三个是大致相等的。有时聚集索引是有害的吗?答案是肯定的,主要与插入,更新和删除行有关。像在这些早期阶段遇到的索引的很多其他方面一样,这也是一个更高级别更详细的主题。

一般来说,检索效益大于维护损害;使聚簇索引更适合堆。如果您要在Azure数据库中创建表,则别无选择。每个表都必须是聚簇索引。

结论

聚集索引是一个有序表,其顺序由您在创建索引时指定,并由SQL Server维护。根据其关键值,该表中的任何行都可以快速访问。在索引键序列中,任何一组行都可以通过键的范围快速访问。

每个表只能有一个聚簇索引。哪些列应该是聚簇索引键列的决定是您将为任何表格做出的最重要的索引决定。

在我们的四级中,我们将重点从逻辑转向物理,介绍页面和范围,并检查索引的物理结构。

可下载的代码

时间: 2024-10-08 00:46:51

聚簇索引:SQL Server索引级别3的相关文章

[翻译]——集群索引:通往SQL Server索引级别3的阶梯

集群索引:通往SQL Server索引级别3的阶梯 By David Durant 本文是楼梯系列的一部分:SQL Server索引的阶梯 索引是数据库设计的基础,并告诉开发人员使用数据库非常了解设计器的意图.不幸的是,当性能问题出现时,索引常常被添加到事后.这里最后是一个简单的系列文章,它应该能让任何数据库专业人员快速"跟上"他们的步伐 这个阶梯的前面的级别提供了一般的索引和非聚集索引的概述.它总结了关于SQL Server索引的关键概念.当请求到达数据库时,无论是SELECT语句还

SQL Server索引简介:SQL Server索引级别1

作者David Durant,2014/11/05(第一版:2011/02/17) 原文链接: http://www.sqlservercentral.com/articles/Stairway+Series/72284/ 该系列 本文是"Stairway系列:SQL Server索引的阶梯"的一部分 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图.不幸的是,当性能问题出现时,索引往往被添加为事后考虑.这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员

阅读查询计划:SQL Server索引级别9

David Durant,2011/10/05 原文链接:http://www.sqlservercentral.com/articles/Stairway+Series/72441/ 该系列 本文是"Stairway系列:SQL Server索引的阶梯"的一部分 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图.不幸的是,当性能问题出现时,索引往往被添加为事后考虑.这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员"快速" 在整个阶

包含列的索引:通往SQL Server索引级别5的阶梯

大卫?杜兰特2011/07/13 该系列 本文是楼梯系列的一部分:SQL Server索引的阶梯 索引是数据库设计的基础,并告诉开发人员使用数据库非常了解设计器的意图.不幸的是,当性能问题出现时,索引常常被添加到事后.这里最后是一个简单的系列文章,它应该能让任何数据库专业人员快速"跟上"他们的步伐 前面的级别引入了集群和非聚集索引,突出了每个方面的以下方面:: 1.表中的每一行都有一个条目(我们注意到这个规则的例外情况将在以后的级别中被覆盖).这些条目总是在索引键序列中. 2.在聚集索

阅读查询计划:楼梯SQL Server索引级别9

通过大卫·杜兰特,2011/10/05 该系列 本文是楼梯系列的一部分:SQL Server的阶梯索引 索引数据库设计的基础,告诉开发人员使用数据库设计者的意图. 不幸的是索引时往往是后加上的性能问题出现. 终于在这里是一个简单的系列文章,应该让任何数据库专业迅速"加速" 在整个楼梯,我们经常说一定执行查询以某种方式; 我们引用生成的查询计划来支持我们的声明. 管理工作室的估计和实际查询计划可以帮助您确定受益,或缺乏,你的索引. 因此,这个级别的目的是给你足够的理解的查询计划,您可以:

包含列的索引:SQL Server索引的阶梯级别5

SQL Server索引的阶梯"的一部分 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图.不幸的是,当性能问题出现时,索引往往被添加为事后考虑.这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员"快速" 前面的级别引入了聚簇和非聚簇索引,突出了以下各方面: ?表中每一行的索引总是有一个条目(我们注意到,这个规则的一个例外将在后面的级别中进行讨论).这些条目始终处于索引键序列中. ?在聚簇索引中,索引条目是表的实际行. ?在非聚集索引中,条目与

SQL Server索引内部结构:SQL Server索引的阶梯级别10

作者David Durant,2012年1月20日 该系列 本文是"Stairway系列:SQL Server索引的阶梯"的一部分 索引是数据库设计的基础,并告诉开发人员使用数据库关于设计者的意图.不幸的是,当性能问题出现时,索引往往被添加为事后考虑.这里最后是一个简单的系列文章,应该使他们快速地使任何数据库专业人员"快速" 在之前的水平上,我们采取了合理的方法来指标,重点是他们能为我们做些什么.现在是时候采取物理方法,检查指标的内部结构;了解索引的内部特性导致了对

SQL Server 索引维护:系统常见的索引问题

在很多系统中,比如本人目前管理的数据库,索引经常被滥用,甚至使用DTA(数据库引擎优化顾问)来成批创建索引(DTA目前个人认为它的真正用处应该是在发现缺失的统计信息,在以前的项目中,用过一次DTA,里面提示了很多列缺少统计信息,后来在不改动其他操作的前提下,把这些统计信息手动建上去,性能提升非常明显.关于统计信息将另开文章介绍).一个表甚至有20多个索引(索引的数量并没有标准,但是要尽量合理,每个索引都应该能支撑大量查询或者增删改中的查询功能才有存在价值).索引过多带来了服务器的沉重压力,有这么

SQL Server索引的维护 - 索引碎片、填充因子 <第三篇>

实际上,索引的维护主要包括以下两个方面: 页拆分 碎片 这两个问题都和页密度有关,虽然两者的表现形式在本质上有所区别,但是故障排除工具是一样的,因为处理是相同的. 对于非常小的表(比64KB小得多),一个区中的页面可能属于多余一个的索引或表---这被称为混合区.如果数据库中有太多的小表,混合区帮助SQL Server节约磁盘空间. 随着表(或索引)增长并且请求超过8个页面,SQL Server创建专用于该表(或索引)的区并且从该区中分配页面.这样一个区被称为统一区,它可以为多达8个相同表或索引的