SQL Server 索引

作者: 2abc3927e84f | 来源:发表于2017-03-24 16:45 被阅读147次

    索引是数据库规划和系统维护的一个关键部分。它们为SQL Server(以及任何其他的数据库系统)提供了查找数据和定位到数据物理位置的快捷方式的其他方法。通过添加正确的索引可以大大减少查询的执行时间。但是,许多设计很差的索引实际上会增加运行所花费的时间。事实上,索引正逐步成为SQL Server中最容易令人误解的对象,因此也是最容易管理不当的对象。

    SQL Server的存储机制

    区段

    区段(extent)是用来为表和索引分配空间的基本存储单元。它由8个连续的8KB数据页组成,共计64KB大小。关于区段的要点包括以下两方面:

    一旦区段已满,那么下一记录将要占据的空间不是记录的大小,而是整个新区段的大小。
    通过预先分配空间,SQL Server节省了为每个记录分配新空间的时间。

    页是特定区段中的分配单元。每个区段包含8页。页是在达到实际数据行之前所能达到的最后一个存储级别。尽管每个区段中的页数据是固定的,但每一页中的行数不是固定的,这完全取决于行的大小,而行的大小是可以变化的。可以把页看作是表行和索引行数据的容器。通常不允许行跨页。

    页拆分

    当页已满时,它会进行行拆分。这意味着多个新页被分配,也以为着现有页上有近半的数据被移到新页上。

    在使用群集索引时,该过程会有例外。如果有一个群集索引,并且下一个插入的记录在物理上作为表中的最后一个记录,那么创建一个新页,然后将该新行添加到这个新页中,而不需要重新定位任何现有数据。在研究索引时将介绍有关拆分的内容。

    理解索引

    韦氏字典中将索引定义为“通常按字母顺序排列的一些指定数据(入作者、主题或者关键字)的列表(如目录信息或者著作正文的引用)”。换种简单的表述,即索引是一种能快速访问数据的方法。

    索引排序规则

    索引中的存储顺序取决于为数据建立的排序规则信息。可以在数据库或列级设置排列规则,因此有相当细粒度的控制级别。一旦设置了排序规则顺序,改变它是非常困难的(但也是可能的),因此在设置之前要确定所需的排序规则顺序。

    平衡树(B-Tree)

    平衡树仅提供了一种以一致相对低成本的方式查找特定信息的方法。其名称中的“平衡”是自说明的。平衡树是自平衡的(只有很少的例外的情况),这意味着每次树进行分支时都有接近一半的数据在一边,而另一半数据在另一边。而名称中的“树”的概念也是很清楚的,如果绘制该结构,然后倒置它,会发现该结构具有树的一般形状,因此称为树。

    平衡树

    更新平衡树:页拆分简介

    所有这些页在读取方面工作良好,但在插入时会有点棘手。回顾一下,“B-Tree”中的“B”表示平衡(Balanced)。而且前面提到每次遇到树种的分支时,因为每一边都大约有一半的数据,所以B-Tree是平衡的。另外,由于添加新数据到树上的方法一般可避免出现不平衡,所以B-Tree有时被认为是自平衡的。

    通过将数据添加到树上,节点最终将变满,并且将需要拆分。因为在SQL Server中一个节点相当于一个页,所以这称为页拆分。如下图所示:

    更新平衡树:页拆分

    当发生页拆分时,数据自动地四处移动以保持平衡。数据的前半部分保留在旧页上,而数据的剩余部分添加到新页,这样就形成对半拆分,使得树保持平衡。

    如果稍微考虑下这个拆分过程,将认识到它在拆分时增加了大量系统开销。不只是插入一页,而是进行下列操作:

    • 创建新页
    • 将行从现有页移动到新页上
    • 将新行添加到其中一页上
    • 在父节点中添加另一个记录项

    但是,系统开销远不止这些。因为在进行树的排列,就可能有级联操作。创建新页时(因为拆分的缘故),需要在父节点中建立另一个记录项。在父节点中的这个及记录项在该级别也可能导致页拆分,而且整个过程会重新开始。实际上,这种可能性扩展到所有节点,甚至影响到根节点。

    如果根节点拆分,那么最终实际会创建两个额外的页。由于只能有一个根节点,所以之前作为根节点的页被拆分为两页,而且成为树的新中间级别。然后创建全新的根节点,并且将有两个记录项(一个指向旧的根节点,另一个指向拆分的页)。

    显然,页拆分会对系统性能产生非常负面的影响,其表现是在服务器上的处理会暂停几秒(此时页被拆分并改写)。

    虽然页级的页拆分时很常见的,但是在中间节点进行页拆分却很少发生。当表增长时,索引的每一层将进行页拆分,但是,因为中间节点对于下一级节点的几个记录只有一个记录项,所以当向树的上层移动时,页拆分的数量将变得越来越少。尽管如此,对于发生在页级以上的拆分来说,在下一个较低级别上一定有一个,这意味着沿树而上的页拆分在本质上是累计的(而且严重影响性能)。

    SQL Server 中的数据访问方式

    从广义上讲,SQL Server检索所需数据的方法只有两种:

    • 使用表扫描:表扫描是相当直观的过程。当执行表扫描时,SQL Server从表的物理起点处开始,浏览表中的每一行。当发现和查询条件匹配的行,就在结果集中包含它们。
    • 使用索引:当SQL Server采用索引时,该过程实际上与表扫描的工作方式相类似,但是有一些捷径。在查询优化过程中,优化器查看所有可以那个的索引结构并选择最好的一个索引(这主要基于在连接和WHERE字句中所指定的信息,以及SQL Server在索引结构中保存的统计信息)。一旦选择了索引,SQL Server将在树结构中导航至与条件匹配的数据位置,并且提取它所需要的记录。区别在于,因为数据是排序的,所以查询引擎知道它何时到达正在查找的当前范围的下界。然后它可以结束查询,或者根据需要移至下一数据范围。

    SQL Server使用何种方法来执行特定查询取决于可用的索引、所需的列、使用的连接以及表的大小。

    索引类型

    尽管表面上在SQL Server中有两种索引结构(群集索引和非群集索引),但是在实际撒很高,就内部而言,有3种不同的索引类型。

    • 群集索引
    • 非群集索引,该索引又包括以下两种:
      • 堆上的非群集索引
      • 群集索引上的非群集索引

    物理数据的存储方式在群集索引和非群集索引中是不同的。而SQL Server遍历平衡树以到达末端数据的方式在所有3中索引类型中也是不同的。

    所有的SQL Server索引都有叶级和非页级页。正如讨论平衡树所提到的那样,叶级是保存标志记录的“键”的级别,非页级是叶级的引导者。

    索引在群集表(如果有群集索引)或者堆(用于没有群集索引)上创建。

    • 群集表:群集表示在其上具有U群集索引的任意表。它们对于表而言意味着以指定顺序物理存储数据。通过使用群集键唯一地标志独立的行,群集键即定义群集索引的列。
    • :堆是在其上没有群集索引的任意表。在这种情况下,基于行的区段、页以及行偏移量(偏移页顶部的位置)的组合创建唯一的标识符,或者成为行ID(Row ID, RID)。如果没有可用的群集键(没有群集索引),那么RID是唯一必要的内容。

    ** 群集索引**

    群集索引对于任意给定的表而言是唯一的,每个表只能有一个群集索引。不一定要有非群集索引,但是如果查看索引类型,你会发现由于多种很显然那的原因,它正成为最常被使用的一种类型。

    使群集索引变得特殊的方面是,群集索引的页级是实际的数据。也就是说,数据重新排序,按照和索引排序条件声明的相同相同物理顺序存储。这意味着,一旦到达索引的页级,就到达了数据。任何新纪录都根据其正确的物理顺序插入到群集索引中。创建新页的方式随需要插入的记录的位置而变化。

    如果新纪录要插入到索引结构中间,就会发生正常的页拆分。来自旧页的后一半记录被移到新页,并且适当的时候,将新纪录插入到新页或旧页。

    如果新纪录在逻辑上位于索引结构末端,那么创建新页,但是只将新纪录添加到新页,如下图。

    新纪录在逻辑上位于索引结构末端

    堆上的非群集索引

    页级不是数据,相反,它是一个可从中获得指向该数据的指针的级别。该指针以RID的形式出现,如同在本章前面描述的那样,这种RID由索引指向的特定行的区段、页以及偏移量构成。即使叶级不是实际的数据(相反,它具有RID),使用叶级也仅仅比使用群集索引多一个步骤。因为RID具有行的位置的全部信息。所以可以通过RID直接达到数据。

    然而,不要误以为“这个额外步骤”只有少量的系统开销,并且堆上的非群集索引将和群集索引几乎一样快的运行。使用群集索引,数据在物理上是按照索引的顺序排列的。这意味着,对于一定范围的数据,当找到在其上具有数据范围起点的行时,那么很可能有其他行 在同一页上(也就是说,因为它们存储在一起,所以在物理上已几乎到达下一个记录)。使用堆,数据并为通过除索引外的其他方法链接在一起。从物理上看,绝对没有任意种类的排序。这意味着,从物理读取的角度看,系统可能不得不从整个文件中检索记录。实际上,很可能最终多次从同样的页中取出数据。SQL Server没有方法知道它需要回到该物理位置,因为在数据之间没有链接。使用群集索引,它知道这是物理上的排序,因此仅仅通过访问页一次就完全获得数据。

    堆上的非群集索引查找

    如上图所示。主要通过索引导航,但一切都按以前的方式工作。服务器从相同的根节点开始,并且遍历树,处理越来越聚焦的页,直到到达索引的叶级。这里就有了区别。采用群集索引的方式,能够正好在这里停止,而采用非群集索引的方式则需要做更多的工作。如果非群集索引是在堆上,那么只要再进入一个级别。获得来自叶级页的RID,并且定位到该RID,直到这时才可以直接获得实际的数据。

    群集表上的非群集索引

    和堆上的非群集索引一样,索引的非叶级节点的工作与使用群集索引时相比几乎一样。区别出现在叶级。

    在叶级,与使用其他两种索引结构所看到的内容有相当明显的区别:有另外一个索引来查找。使用群集索引,当服务器达到叶级时,它可以找到实际的数据。使用堆上的非群集索引,不能找到实际的数据,但是可以找到能够直接获得数据的标识符(仅仅多了一步)。使用群集表的非群集索引,可以找到群集键。也就是说,服务器找到足够的信息来利用群集索引。

    如下图。服务器首先执行范围搜索,这一点与前面相同。在索引中执行一次单独的查找,并且可以浏览非群集索引以找到满足条件(LIKE'T%')的连续数据范围。这种能够直接到达的索引中的特定位置的查找称为seek。

    查找FName类似“T%”的EmployeeID

    然后开始第二个查找,使用群集索引的查找。第二种查找非茶馆迅速:问题在于它必须执行多次。可以看到,SQL Server从第一个索引查找中索引列表(所有名称以“T”开始的列表),但是该列表在逻辑上并没有以任意连续的方式与群集键相匹配,每个记录需要单独地查找,如下图。

    ![Uploading image_980906.png . . .]

    创建、修改和删除索引

    CREATE INDEX语句

    CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED]
    INDEX <index name> ON <table or view name>(<column name> [ASC | DESC] [, ...n]
    [WHERE <condition>])
    [WITH
    [PAD_INDEX = { ON | OFF }]
    [[, ] FILLFACTOR = <fillfactor>]
    [[, ] IGNORE_DUP_KEY = { ON | OFF }]
    [[, ] DROP_EXISTING = { ON | OFF }]
    [[, ] STATISTICS_NORECOMPUTE = { ON | OFF }]
    [[, ] SORT_IN_TEMPDB = { ON | OFF}]
    [[, ] ONLINE = { ON | OFF }]
    [[, ] ALLOW_ROW_LOCKS = { ON | OFF }]
    [[, ] MAXDOP = <maxinum degree of parallelism>]
    [[, ] DATA_COMPRESSON = { NONE | ROW | PAGE }]
    ]
    [ON {<filegroup> | <partition scheme name> | DEFAULT}]
    

    随约束创建的隐含索引

    当向表中添加如下两种约束之一时,就会创建隐含索引:

    • 主键约束
    • 唯一约束(也称为替换键)

    当创建一个索引作为约束的隐含索引时,除{CLUSTERED | NONCLUSTERED}和FILLFACTOR外,所有选项都不允许使用。

    创建XML索引

    XML索引是SQL Server 2005中的新增功能。除了WHERE、IGNORE_DUP_KEY和ONLINE外,XML索引创建语法支持在前面的CREATE语句中所看到的所有相同选项。

    在SQL Server中,可以在类型为XML的列上创建索引。这样做的主要要求如下:

    • 在包含需要索引的XML的表上必须具有群集索引。
    • 在创建“辅助”索引之前,必须现XML数据列上创建“主”XML索引。
    • XML索引只能在XML类型的列上创建(而且XML索引是可以在该类型的列上创建的唯一一种索引)。
    • 主索引必须是基表的一部分,不能在视图上创建索引。

    主XML索引:在XML索引上创建的第一个索引必须声明为“主”索引。当创建主索引时,SQL Server创建一个新的群集索引,这个群集索引将基表的群集索引和来自任何指定的XML节点的数据组合在一起。

    辅助XML索引:这里没有任何特别之处,非常类似指向群集索引的群集键的非群集索引,辅助XML索引以相似的方法指向主XML索引。一旦创建了主XML索引,就能在XML列上创建多达248个以上的XML索引。

    在稀疏列和地理空间列上创建索引
    由于其复杂度,这里不过多说明。但事实是可在稀疏列和地理空间类型数据上创建特殊索引。因此,如果要应用这些特殊需求类型的列,就需要将此谨记在心。

    明智地选择——在何时何地使用何种索引

    选择性

    索引,特别是非群集索引,主要在其中有相当高级别的选择性的情况下是有益的。所谓选择性,指的是列中唯一值得百分比。列中唯一值得百分比越高,选择性就越高,从而索引的益处就越大。

    ** 注意成本:少即是多**

    记住,虽然索引在读取数据方面可提高性能,但是在修改数据时,它们实际上花费很高。索引没有通过魔法来维护,每次对数据进行修改时,任何与该数据相关的索引也需要更新。可以将执行更新时花费的时间认为是投资的金钱。每次通过索引读取数据时,您 可以将节省的时间视为投资回报:但是对于没有使用的每个索引,投资就没有任何回报。

    选择群集索引

    记住,只可以有一个群集索引,所以需要明智地选择它。

    默认情况下,主键是和群集索引一起创建的。这通常是个不错的选择,但并不总是如此(实际上,在有些情况下,这回带来严重的危害),并且如果这样做,那么 将不能在其他任何地方使用群集索引。这里的要点在于不要接受默认方式。在定义主键时要考虑一下,确实想要它作为群集索引吗?

    如果确实想要改变,也就是说,不想声明为群集索引,那么在创建表时只需要添加NONCLUSTERED关键字。例如:

    CREATE TABLE MyTableKeyExample
    (
      Column1 int IDENTITY
        PRIMARK KEY NONCLUSTERED,
      Column2 int
    )
    

    如果讨论的列常作为范围查询的对象,那么群集索引对于这类查询是很用的。这类查询通常使用BETWEEN语句或者<or>符号。使用GROUP BY以及可利用MAX、MIN和COUNT聚合函数额查询也是使用范围和偏好群集索引的查询的重要示例。群集索引适用此处,这是因为搜索可以直接到达物理数据中的特定点,可一直读数据,直到到达范围的末端,然后停止。这种方法非常有效。当想要数据基于群集排序(使用ORDER BY)时,群集也是极好的方法。

    在将要以非连续的顺序进行大量插入时不适合使用群集索引。还记得页拆分的概念吗?这里会进行叶拆分,并且会消耗大量时间。

    列顺序问题

    仅仅因为索引中有两个列,这不能说明索引对于任何引用其中一列的查询是有用的。

    如果查询中使用了索引中列出的第一个列,那么可考虑使用该索引。好的方面是不必在每一列上一对一匹配——只需要第一个列匹配。当然,(按照顺序)匹配的列越多越好,但只需要通过第一个列就可确定“不要使用”某索引。

    可以这样考虑一下,假设在使用电话薄。所有项都按先姓后名的方式进行索引。如果知道要通话电话的人的名是Fred,那么这种排列顺序能带来任何好处吗?另一方面,如果只知道他的姓是Blakc,那么索引将可以用来缩小查找范围。

    索引构造过程中较为常见的一种错误是认为一个包含所有列的索引将对任何情况都是有帮助的。实际上,这样做只是将所有数据又存储了一次。如果索引的第一个列没有在查询中的JOIN、ORDER BY或者WHERE子句中提及,那么索引将完全被忽略。

    覆盖索引

    简单来说,覆盖索引包含查询所需要的所有数据。如果前面看到的那样,一旦SQL Server发现它需要的数据,就会停止查找。建立在索引基础的几乎所有最终查找都采用这种处理方式。如果只是仅需要在一个索引键中解析查询,或者需要将其包含在叶子中,就没有理由执行这种查找。

    最基础和最显而易见的覆盖索引是群集索引。表中的所有数据都在叶子中,因此群集覆盖了所有查询。

    您可以在索引的叶子中包含(INCLUDE)非键列。尽管这看起来可以解决群集键查找的任何问题,但是它需要一定的开销。你的索引键保持相同的大小,因此查找速度依然很快,但是叶子必须增大以容纳额外的数据。此外,每次插入、更新或删除数据时,必须抛弃更多的位数进行补偿。当然,对于必须快速执行的常见查询,这是极好的工具。

    过滤索引

    到目前为止看到的所有索引有一个共同点:每个索引都针对表中的每一行在叶子中有一个条目。然而,这并不是严格的必要条件,并且有时需要限制出现在索引中的行。

    创建过滤索引只需要包括WHERE字句。

    您可以在运行包括兼容WHERE表达式的查询时使用该索引。关于过滤索引需要注意一下几点:

    • 索引深度远小于全表索引。您只是索引几千行,而不是索引超过十万行,因此遍历索引的速度更快。
    • 因为索引只包含条件过滤后的结果集,所以通过插入、更新、删除操作维护该索引的开销较低。改变不存在过滤后的结果集中的数据完全不影响索引。

    过滤索引的一个相对常见用途是有效地允许在可为NULL的列上设置唯一约束。通过使用WHERE <column> IS NOT NUL 字句创建唯一索引,您可以阻止重复的实际数据,并且仍然允许存在的NULL值。

    修改索引

    如果修改索引的组成,那么仍然需要DROP(删除)然后CREATE(创建)索引,或者用DROP_EXISTING=ON选项CRAETE(创建)并使用索引。

    ALTER INDEX的语法如下。

    ALTER INDEX { <name of index> | ALL }
      ON <table or view name>
      { 
        REBUILD
        [  [  WITH (
              [ PAD_INDEX = { ON | OFF }  ]
              | [ [, ] FILLFACTOR = <fillfactor> ]
              | [ [, ] SORT_IN_TEMPDB = { ON | OFF } ]
              | [ [, ] IGNORE_DUP_KEY = { ON | OFF } ]
              | [ [, ] STATISTICS_NORECOMPUTE = { ON | OFF } ]
              | [ [, ] ONLINE = { ON | OFF } ]
              | [ [, ] ALLOW_ROW_LOCKS = { ON | OFF } ]
              | [ [, ] ALLOW_PAGE_LOCKS = { ON | OFF } ]
              | [ [, ] MAXDOP = <max degree of parallelism> ]
              | [ [, ] ONLINE = { ON | OFF } ]
            )]
            | [ PARTITION = <partition number>
                [ WITH ( 
                  <partition rebuild index option>
                  [, ...n]
               )]
              ]
            ]
            | DISABLE
            | REORGANIZE
              [ PARTITION = <partition number> ]
              [ WITH ( LOB_COMPACTION = { ON | OFF } ) ]
            | SET (
              [ ALLOW_ROW_LOCKS = { ON | OFF }] 
              | [ [, ] ALLOW_PAGE_LOCKS = { ON | OFF } ]
              | [ [, ] IGNORE_DUP_KEY = { ON | OFF } ]
              | [ [, ] STATISTICS_NORECOMPUTE = { ON | OFF } ]
              )
      }  [ ; ]
    
    • 索引名:如果项维护一个特定的索引,可以指定该索引,或者使用ALL表明项要维护与指定的表相关联的所有索引。

    • 表明或视图名: 从字面可知,这是想要在其上执行维护的特定对象(表或视图)的名称。注意,必须是一个特定的表(可以给它提供一个列表,然后说“请处理所有这些”)。

    • REBUILD:这是用来修复索引的“工业级强度”的方法。如果使用该选项运行ALTER INDEX,那么将完全丢弃旧的索引并重新生成新的索引。结果是真正优化的速印,其中所有叶级和非叶级的页都按照定义进行了重新构建(使用个默认值或者开关修改填充因子等)。如果讨论中的索引是群集索引,那么也会重新组织物理数据。
      要小心使用该选项。一旦开始REBUILD,在完成索引重建之前,正在使用的索引实际就没有了。依赖该索引的所有查询可能变得异常缓慢(可能会降几个数量级)。对于这类事情,首先需要在离线系统上测试,以了解整个过程将花多少时间。然后,计划在非高峰时段运行(最好有人监控,以确保它在高峰时段来临时恢复联机状态)。

    • DISABLE:该选项名副其实,只是方式有点过激。如果该命令的全部作用只是为了让索引离线,直至您决定了进一步要做什么,则它是不错的选择,但它实际上会把索引标记为不可用。一旦禁用了某个索引,在重新激活之前,必须重建索引(不是重新组织,而是重建)。ALTER INDEX...DISABLE的相反操作并不是ENABLE,这样的操作并不存在。你必须执行ALTER INDEX...REBUILD。
      如果对表禁用了群集索引,那么也会禁用表。数据仍会保留,但在重建群集索引之前,不能被所有索引(因为它们都依赖于群集索引)访问。

    REORGANIZE

    从开发人员的角度看,这一选项特别好。如果重新组织索引,就得到了比完全重建索引稍逊一点的完全优化,但这种方法可以联机进行(用户仍能使用索引)。

    如果仔细琢磨,那么上面的描述可能会让你想到一个问题,“稍逊一点”到底是指什么。它其实指,REORGANIZE只是在索引的叶级起作用,而不触及非叶级。这意味着未获得完全优化。但是,对于大部分的索引而言,那不是真正产生碎片的地方(尽管可能会发生这种情况,并且遇到的情况也不尽相同)。

    由于该选项对用户的影响非常小,通常您会希望该工具作为常规维护计划的一部分来使用。后面讨论碎片时将更进一步讨论它。

    删除索引

    如果正在不断地重新分析情况和添加索引,那么也不要忘记删除索引。记住插入索引需要系统开销。由于在考虑需要的索引时并没有对插入的开销太在意,因此也没有考虑过那些索引是不需要的。总是要自问一下:“可以从中去掉那些索引?”

    删除索引的语法非常类似于删除表的语法。唯一的不同在于需要使用索引所附着的表或视图来限定索引名称:

    DROP INDEX <table or view name>.<index name>
    

    或者

    DROP INDEX <index name> ON <table or view name>
    

    这样就可以删除索引。

    从查询计划中获取提示

    SQL Server 2008提供了一个新功能:查询计划信息中的索引提示,它们将给出查询优化器所认为有用的、但不存在的索引(一次给出一个索引)。现实情况是,在创建建议的索引之后,您不需要严格地检查查询是否使用该索引;即使该索引不会被任何其他的查询再次使用,他也会用于查询。如果您在执行某个重要的查询时获得该提示,则在大多数情况下需要采取该提示的建议。

    索引未被使用的原因

    反复的测试!检查您的索引是否被使用。如果它们未被使用,则开始查找原因,比如无序的WHERE子句、缺乏选择性、建议的索引或不可索引条件。

    当你的WHERE子句中过滤的是某个函数而不是列时,就不会用索引。

    维护索引

    实际上,就索引的维护而言有以下两个问题需要处理:

    • 页拆分
    • 碎片

    这两个问题和页密度相关,虽然两者的表现形式在本质上有区别,但是故障排除工具是一样的,因为处理也是相同的。

    碎片

    当数据库增长而执行页拆分,然后最终删除数据时,就会产生碎片。虽然从增长的观点看,平衡树机制在保持平衡方面做得还不错,但在删除数据时,它并没有太多作用。最终,可能出现这样一种情况:在这一页上有一个记录,而在那一页上有几个记录,在这种情况下,许多数据页上的数据量只是它们可以保存的总数据量的一小部分。

    关于碎片首先会想到的第一个问题是,浪费空间。前面提到过,SQL Server每次分配一个区段的空间。如果一个页上只有一条记录,则仍然会分配整个区段。

    第二个问题是,散布在各处的记录会造成数据检索时的额外的系统开销。为了获取需要的10行记录,SQL Server不是只加载一页,而是可能必须加载10个不同的页来获取相同的信息。并不只是读取行导致了这一结果,SQL Server必须读取该页,更多的页意味着更多的读取工作量。

    虽说如此,数据库碎片也有它好的一面,OLTP系统就喜欢碎片。原因是什么呢?页拆分。没有许多数据的页在插入数据时几乎或完全不用担心也拆分。

    所以,大量的碎片意味着较差的读取性能,但是它也意味着极好的插入性能。正如您所预料的,这意味着OLAP系统实际不喜欢碎片。

    小结

    在SQL Server或者任何其他数据艰苦环境中,索引都是一个基本的主题,而且不可轻视。它们有助于获得良好的性能,但也可能 导致极差的性能。

    关于索引要考虑的几个首要问题如下:

    • 群集索引通常比非群集索引快(可以说前者总是比后者块,但是也有一些例外)。
    • 仅在将得到高级别选择性的列(也就是说,95%或者更多的行是唯一的)上放置非群集索引。
    • 所有的数据操作语言(DML : INSERT、UPDATE、DELETE、SELECT)语句可以通过索引获益,但是插入、删除和更新(记住,它们使用删除和插入方法)会因为索引而变慢。索引有助于查询的查找过程,但是任何修改数据的行为将有额外的工作要做(除了实际数据外,还要维护索引)。
    • 索引会占用空间。
    • 仅当索引中的第一列和查询相关时才使用索引。
    • 索引的负面影响和它的正面影响一样多,了解为什么建立索引,以及为什么只建立需要的索引。
    • 索引可为非结果化XML数据提供结构化的数据性能,但是要记住,和其他索引一样,这回涉及系统开销。

    考虑索引是可以问自己这样一些问题:

    • Q : 会对这个表进行大量插入或者修改吗?
      A : 如果是,尽量少用索引。这种索引的表通常通过主键的单个记录查找 完成修改,这往往是该表上需要的唯一索引。如果插入时非连续的,不考虑使用群集索引。
    • Q : 这是报表吗?也就是说,这里没有许多插入,但是会以许多不同的方法运行报表吗?
      A : 有更多索引是好的。将群集索引确定为频繁使用的、可能会在范围内提取的信息。OLAP系统中的索引数量是OLTP环境中所看到的许多倍。
    • Q : 在数据上有高级别的选择性吗?
      A : 如果是,而且它通常是WHERE子句的目标,那么添加索引。
    • Q : 已经删除不再需要的索引了吗?
      A : 如果没有,为什么不删除?
    • Q : 已建立了维护策略吗?
      A : 如果没有,为什么不建立?

    参考

    《SQLServer2012编程入门经典(第4版)》

    相关文章

      网友评论

        本文标题:SQL Server 索引

        本文链接:https://www.haomeiwen.com/subject/ffyznttx.html