原文连接:http://www.sqlservercentral.com/articles/Stairway+Series/72351/javascript
经过大卫·杜兰特,2013/01/25(第一次出版:2011/06/22)java
本文是楼梯系列的一部分:SQL Server的阶梯索引sql
索引数据库设计的基础,告诉开发人员使用数据库设计者的意图。 不幸的是索引时每每是后加上的性能问题出现。 终于在这里是一个简单的系列文章,应该让任何数据库专业迅速“加速”数据库
前面的水平在这个楼梯概述了非汇集索引的索引通常和特别。 结论用如下关于SQL Server关键概念索引。 当一个请求到达您的数据库,一个SELECT语句或一个INSERT、UPDATE或DELETE语句,SQL Server只有三种可能的方法来访问数据表中引用的声明:数据结构
这个水平首先关注的第三选择上面的列表; 搜索表。 反过来,这将致使咱们的讨论集群索引; 一个被说起的话题,但没有涵盖,在2级。数据库设计
主AdventureWorks咱们将使用的数据库表是在这个水平SalesOrderDetail表。 在121317行,足以说明一些好处有汇集索引的表。 有两个外键,它是复杂的足以说明一些设计决策,你必须使你的汇集索引。sqlserver
尽管咱们已经讨论了在一级sample数据库,值得重复。 在整个楼梯,咱们将使用示例来讲明概念。 这些例子是基于微软的AdventureWorks示例数据库。 咱们专一于销售订单。 五表将给咱们一个良好的事务性和非事务性数据;客户,销售人员,产品,SalesOrderHeader,SalesOrderDetail。 为了保持专一,咱们使用列的一个子集。 由于AdventureWorks规范化,销售人员信息分解成三个表:销售人员,员工和联系。性能
整个楼梯咱们使用如下两个术语,指一行订单互换:“行项目”和“订单细节”。 前者是更常见的业务术语; 后者的名字出如今一个AdventureWorks表。spa
完整的一套表,和它们之间的关系,如图1所示。设计
图1:表中使用的例子在这个楼梯
注意:
全部TSQL代码所示这楼梯水平随着文章能够下载。
咱们先问如下问题:有多少工做须要找到表中的一行(s)若是不使用非汇集索引? 搜索请求的行表意味着扫描一个无序表中每一行吗? 永久或SQL Server序列表的行,以便它能够快速访问他们的搜索键,就像快速访问非汇集索引的搜索键的条目吗? 答案取决于你是否指示SQL Server上建立一个汇集索引表。
与非汇集索引是一个单独的对象,占据本身的空间,汇集索引和表是同样的。 经过建立汇集索引,您指示SQL Server排序表的行索引键序列,在将来保持序列数据的修改。 即将到来的水平会看看生成的内部数据结构来完成这个。 可是如今,想到一个汇集索引排序表。 鉴于连续索引键值,SQL Server能够快速访问这一行; 而且能够经过表的行顺序进行。
出于演示的目的,咱们建立两个咱们的示例表的副本,SalesOrderDetail; 一个没有索引,一个汇集索引。 关于索引的键列,咱们的设计师作出一样的选择AdventureWorks数据库:SalesOrderID/SalesOrderDetailID。 清单1中的代码的副本SalesOrderDetail表。 咱们能够随时从新运行这段代码,咱们但愿从一个“白纸”开始。
若是存在(选择*从sys.tables&# 160;在哪里OBJECT_ID=OBJECT_ID(“dbo.SalesOrderDetail_index”))删除表dbo.SalesOrderDetail_index;去若是存在(选择*从sys.tables&# 160;在哪里OBJECT_ID=OBJECT_ID(“dbo.SalesOrderDetail_noindex”))删除表dbo.SalesOrderDetail_noindex;去选择*成dbo.SalesOrderDetail_index从Sales.SalesOrderDetail;选择*成dbo.SalesOrderDetail_noindex从Sales.SalesOrderDetail;去建立汇集索引IX_SalesOrderDetail在dbo.SalesOrderDetail_index(SalesOrderID,SalesOrderDetailID)去
清单1:建立SalesOrderDetail表的副本
因此,假设SalesOrderDetail表建立汇集索引前是这样的:
SalesOrderID SalesOrderDetailID ProductID OrderQty UnitPrice
38.10 69389 102201 864 3
34.99 56658 59519 711 1
59044 70000 956 2 1430.442
44.994 48299 22652 853 4
44.994 50218 31427 854 8
34.99 53713 50716 711 1
744.2727 50299 32777 739 1
2024.994 45321 6303 775 6
2.29 72644 115325 873 1
141.615 48306 22705 824 4
120.00 69134 101554 876 1
469.794 48361 23556 760 3
602.346 53605 50098 888 1
183.9382 48317 22901 722 1
8.99 66430 93291 872 1
65281 90265 889 2 602.346
9.99 52248 43812 871 1
47978 20189 794 2 1308.9375
建立上面所示的汇集索引后,生成的表/集群指数看起来像这样:
SalesOrderID SalesOrderDetailID ProductID OrderQty UnitPrice
178.58 43668 106 722 3
20.19 43668 107 708 1
356.90 43668 108 733 3
419.46 43668 109 763 3
714.70 43669 110 747 1
5.70 43670 111 710 1
43670 112 709 2 5.70
43670 113 773 2 2039 .99点
43670 114 776 1 2024 .99点
43671 115 753 1 2146 .96点
43671 116 714 2 28.84
874.79 43671 117 756 1
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
28.84 43671 123 716 1
20.19 43671 124 711 1
20.19 43671 125 708 1
5.70 43672 126 709 6
43672 127 776 2 2024 .99点
43672 128 774 1 2039 .99点
874.79 43673 129 754 1
28.84 43673 130 715 3
183.94 43673 131 729 1
你看上面所示的示例数据,您可能会注意到,每个SalesOrderDetailID价值是独一无二的。 不要混淆;SalesOrderDetailID不是表的主键。 的结合SalesOrderID/SalesOrderDetailID是表的主键; 以及汇集索引的索引键。
汇集索引键能够由你选择的任何列; 它不须要基于主键。 在咱们的例子中,最重要的是,最左侧列的关键是一个外键,SalesOrderID价值。 所以,销售订单的全部行项目中连续出现SalesOrderDetail表。
记住这些额外的点对SQL Server集群索引:
能够有最多每一个表一个汇集索引。 一个表的行能够在只有一个序列。 你须要决定什么序列,若是有的话,最好为每一个表; 若是可能的话,建立汇集索引表变得充满了以前的数据。 作这个决定时,请记住,测序不只意味着订购,这也意味着分组; 在分组由销售订单行项目。
这就是为何的设计者AdventureWorks数据库的选择SalesOrderDetailID在SalesOrderID的序列SalesOrderDetail表; 行项目的天然顺序。
例如,若是一个用户请求一个订单的行项目,他们一般会要求全部订单的行项目。 看一个典型的销售订单的形式告诉咱们,订单的打印副本老是包括全部行项目。 它的本质是销售订单业务集群由销售订单行项目。 可能会有偶尔的请求从仓库想看产品而非销售订单行项目; 但大多数的请求; 如来自销售人员或客户,或程序,打印发票,或查询,计算每一个订单的总价值; 须要任何销售订单的全部行项目。
用户需求,然而,不肯定什么是最好的汇集索引。 本系列之后的水平将覆盖的内部索引; 由于某些内部方面的汇集索引列的索引也会影响你的选择。
若是没有汇集索引表,该表称为堆。 每一个表都是一堆或一个汇集索引。 因此,尽管咱们常常状态的每一个索引分为两种类型,集群或非汇集; 一样重要的是要注意,每一个表分红两个类型; 它是一个汇集索引或一堆。 开发人员常常说一个表“有”或“没有”一个汇集索引,但它是更有意义的说表”是”或“不是”一个汇集索引。
只有一种方法为SQL Server来搜索一个堆在寻找行(不含非汇集索引的使用),这是开始在表中的第一行并经过表进行,直到全部的行已经阅读。 没有一个序列,没有搜索键,没有办法快速导航到特定的行。
汇集索引的性能评价和一堆,清单1的两个副本SalesOrderDetail表。 一份是堆版,另外一方面,咱们建立在原始表的汇集索引(SalesOrderID,SalesOrderDetailID)。 表都没有任何非汇集索引。
咱们将运行相同的三个查询每一个版本的表; 一个检索一行,另外一个检索单个订单,全部行和检索单个产品的全部行。 咱们如今每一个执行的SQL和结果表中所示。
咱们的第一个查询检索一行和执行细节如表1所示。
SQL |
SELECT * |
堆 |
(1行受影响) |
汇集索引 |
(1行受影响) |
汇集索引的影响 |
从1495年读3读IO减小。 |
评论 |
没有惊喜。 表扫描121317行找到一个不是很是有效。 |
表1:检索单个行
咱们的第二个查询检索全部行一个销售订单,你能够看到表2的执行细节。
SQL |
SELECT * |
堆 |
(11行受影响) |
汇集索引 |
(11行受影响) |
汇集索引的影响 |
从1495年读3读IO减小。 |
评论 |
与前面的查询相同的统计数据。 堆仍然须要表扫描,而分组的汇集索引11细节行所要求的订单足够近以便检索所需的IO 11行是同样的IO须要检索一行。 即将到来的水平将详细解释为何没有额外的读取须要检索额外的10行。 |
表2:检索单个SalesOrder全部行
和咱们的第三个查询检索一个产品的全部行,执行结果如表3所示。
SQL |
SELECT * |
堆 |
(228行受影响) |
汇集索引 |
(228行受影响) |
汇集索引的影响 |
汇集索引版本IO略大; 1513读与1495读。 |
评论 |
没有ProductID上非汇集索引列帮助找到一个单一产品的行,两个版本必须扫描。 由于有一个汇集索引的开销,略大表的汇集索引版本; 所以扫描须要几堆比扫描读取。 |
表3:检索单个产品的全部行
咱们的第一个两个查询从汇集索引的存在极大地受益; 第三个是大体相等。 是时候有一个汇集索引是损害? 答案是确定的,它主要是与插入、更新和删除行。 像许多其余方面的索引中遇到这些早期的水平,这也是一个主题,将更详细地介绍一个更高的水平。
通常来讲,获取利益大于维护形成损害的; 汇集索引比一堆。 若是你是在一个Azure数据库中建立表,你没有选择的余地; 每一个表必须是一个汇集索引。
汇集索引是一个排序表的序列是由您建立索引时,指定由SQL Server和维护。 该表中的任意行快速访问给定键值。 任何一组行,在索引键序列,也很快访问给定的范围键。
每一个表只能有一个汇集索引。 的决定应该是汇集索引的列索引键列是最重要的决定,你会让任何表。
在咱们的四级咱们将把咱们的重点从逻辑到物理,介绍页面和区段,并检查指标的物理结构。