原文地址:

Stairway to SQL Server Indexes: Level 6,Bookmarks

本文是SQL Server索引进阶系列(Stairway to SQL Server Indexes)的一部分。

在前面的章节中我们看到了,索引就是一些有序入口的集合,每行一个入口。我们更多的强调索引的逻辑方面,而不是物理方面。当目前为止,我们讲述了非聚集索引入口的两个组件:查询键和包含列。在本章中,我们介绍第三个,也是最后一个组件:标签。

什么是标签

标签在之前已经提到过,但只是说SQL Server可以用它快速的从非聚集索引的入口导航到对应的行。现在,是时候介绍更多的细节了。标签的内容会根据表示表示堆表还是聚集索引表,而不同。

不管表是堆表还是聚集索引表,表中的每一行都会位于页的一行。页可能是数据文件的第n页。文件可能是组成数据库的文件集合中的第n个文件。因此,数据库中的每一行,在任何时间,都可以用三个数字来标识:文件号-页号-行号。这三个数字的复合标识叫做rowid,通常叫做RID。大部分可以显示SQL Server内部信息的工具都会显示这三个号码,用分号分隔。因此文件1的77页的12行,RID会显示成1:77:12。

通常来讲,一个堆表的行是不会移动的,一旦他们被插入一页,他们会保持在这页中。更加精准的说法是:在堆表中的行很少移动,当他们移动的时候,在旧的位置上会留下新的地址。聚集索引表的行,是可以移动的,在修改数据或者是维护索引的时候可能会分配到另外一页。在修改数据的时候发生的细节,还有新行的信息,都会在后面的级别中介绍。

因为堆表的行不会移动,在堆表中RID永久的标识每一行。不仅值是永久的,而且物理位置也是永久的。因此在堆表的非聚集索引中,使得它成为理想的标签值,这也是SQL Server在所有的堆表的所有非聚集索引中使用它的原因。

一个堆表的非聚集索引:RID为基础的标签

SalesOrderDetail表是一个堆表,行没有特定的顺序。我们创建一个第五级中介绍的索引,在ProductID和ModifiedDate上的非聚集索引,而且还有包含列。

CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate
ON Sales.SalesOrderDetail(ProductID, ModifiedDate)
INCLUDE (OrderQty, UnitPrice, LineTotal)

入口的顺序是下面的样子。

:- Key Columns -:      :---  Included Columns  ---:          : Bookmark :

ProductID   ModifiedDate      OrderQty UnitPrice LineTotal       
----------- ------------      -------- --------- --------- -------------

Page n-1:

709         01 Feb 2002       1            5.70       5.70      3:9198:41
709         01 May 2002       1            5.70       5.70      3:969:2
710         01 Jul 2001       1            5.70       5.70      3:9840:29
710         01 Jul 2001       1            5.70       5.70      3:9916:29
710         01 Sep 2001       1            5.70       5.70      4:12331:32

Page n:

710         01 Oct 2001       1            5.70       5.70      3:1911:33
710         01 Nov 2001       1            5.70       5.70      4:2604:34
710         01 Nov 2001       1            5.70       5.70      4:2889:34
710         01 Nov 2001       1            5.70       5.70      3:3522:35
710         01 Nov 2001       1            5.70       5.70      3:3623:35

710         01 Jun 2002       1            5.70       5.70      4:3917:5
712         01 Jul 2001       1            5.19       5.19      3:9886:29
712         01 Jul 2001       1            5.19       5.19      3:10270:29
712         01 Aug 2001       1            5.19       5.19      4:10609:30
712         01 Aug 2001       1            5.19       5.19      4:10617:30

Page n+1:

712         01 Aug 2001       1            5.19       5.19      4:10689:30
712         01 Aug 2001       1            5.19       5.19      4:10885:30
712         01 Aug 2001       1            5.19       5.19      4:11002:30
712         01 Sep 2001       1            5.19       5.19      4:12318:32
712         01 Sep 2001       1            5.19       5.19      4:509:32

索引中每一行的标签都是很有效的,直接指向对应的数据行,但是这需要依赖于表是否堆表。尽管这些值对于查询行非常有效,但是他们不包含用户需要的任何信息。

这种RID为基础的标签的替代者是,在已经有聚集索引的表上建立非聚集索引,简洁并准确的说,这种标签就是一种聚集索引的非聚集索引标签。

聚集索引的非聚集索引:键为基础的标签

如果一张表包含聚集索引,表中的行会被重新定位。因此,对于聚集索引来说,RID不是行的永久标识,非聚集索引的标签值需要使用一个不同的值。这个值就是聚集索引的索引键。

这解决了标签值一致性的需要。因为当聚集索引的一行被移动到新页的时候,它只是被移动,而没有被修改。聚集索引的键值没有改变。因此,标签的值总是可以用来获取对应的行,这意味着是通过聚集索引获取行,而不是通过物理位置获取行。

但是,这种聚集索引的键作为非聚集索引的标签的用法,意味着聚集索引的键应该满足三个条件:

  • 它必须是唯一的。每个索引入口的标签,都是SQL Server用来查找入口对应的一行数据的。如果你创建的聚集索引不唯一,SQL Server就需要为重复的键创建额外的信息。这些SQL Server创建的额外信息被叫做“uniquifier”,对客户端应用来说是透明的。对于是否允许重复的聚集索引,你应该很小心的考虑下面两个因素:
  • 它应该很短小。它应该占用少量的字节,因为它将被所有的非聚集索引包含。将表Contact的last name/first name/middle name/street address作为聚集索引的列看起来是个好主意,但是如果表Contact有多个非聚集索引,这就不是一个好主意了。如果包含n个非聚集索引,那么last name/first name/middle name/street address的信息就会保存n+1份。
  • 它应该是静态的。它的值应该很少改变。聚集索引键值的改变,会导致每一个非聚集索引中对应行的入口发生更新操作。因此,如果一张表有n个非聚集索引,一次索引键的更新,会变成n+1次的更新。

AdventureWorks设计团队为SalesOrderDetail表选择聚集索引的时候,他们坚持了上面的三条原则。选择SalesOrderID/SalesOrderDetailID作为聚集索引的键,它们短小、静态,并且唯一。把SalesOrderID左右键的左列,尽管SalesOrderDetailID列本身就是唯一的,还是一起作为聚集索引的键。单个订单的所有行都会在相同的页,或者是两页。把SalesOrderID和SalesOrderDetailID即作为表的主键,又作为聚集索引键,消除了在SalesOrderDetailID列单独做索引的需求。没有人会要求查看#7行,他们会查看订单#47386的所有item,或者是订单#47386的#7 item。最重要的,我们已经说过很多次了,将表保持一个顺序,可以对应用提供最好的服务。

现在,如果我们在包含聚集索引的SalesOrderDetail表上创建相同的非聚集索引,索引入口的排序可能就是下面的样子。

:- Key Columns -:      : ---   Included Columns  ---:    :---   Bookmark   ---:

ProductID   ModifiedDate      OrderQty UnitPrice LineTotal      OrderId     DetailId
----------- ------------      -------- --------- ---------      ----------- ----------

Page n-1:

709         01 Feb 2002       1            5.70       5.70      45329       6392
709         01 May 2002       1            5.70       5.70      46047       8601
710         01 Jul 2001       1            5.70       5.70      43670       111
710         01 Jul 2001       1            5.70       5.70      43676       152
710         01 Sep 2001       1            5.70       5.70      44075       1448

Page n:

710         01 Oct 2001       1            5.70       5.70      44303       2481
710         01 Nov 2001       1            5.70       5.70      44484       2853
710         01 Nov 2001       1            5.70       5.70      44499       3006
710         01 Nov 2001       1            5.70       5.70      44523       3346
710         01 Nov 2001       1            5.70       5.70      44527       3400

710         01 Jun 2002       1            5.70       5.70      46365       10183
712         01 Jul 2001       1            5.19       5.19      43673       136
712         01 Jul 2001       1            5.19       5.19      43694       342
712         01 Aug 2001       1            5.19       5.19      43846       524
712         01 Aug 2001       1            5.19       5.19      43847       528

Page n-1:

712         01 Aug 2001       1            5.19       5.19      43851       567
712         01 Aug 2001       1            5.19       5.19      43863       672
712         01 Aug 2001       1            5.19       5.19      43871       735
712         01 Sep 2001       1            5.19       5.19      44074       1441
712         01 Sep 2001       1            5.19       5.19      44109       1729

哪一种更好

RID标签在表中查询行将更快速,但是索引可能会没有覆盖查询。索引键的标签在进行表查询的时候会较慢,但是增加了索引覆盖查询的可能性,通常会包含做JOIN时候对于外键的需要。

问题“哪一种跟好?”的答案是:都不好。在索引表的时候,最重要的决定是:表的聚集索引是什么?一旦建立了聚集索引(根据文章中的三条规则),你就不需要担心非聚集索引带来的影响,他们都会执行的很好。

结论

非聚集索引的入口由查询键列、包含列、标签组成。标签的值既可以是RID,也可以是聚集索引的键,这依赖于表是堆表还是聚集索引表。为表选择最好的聚集索引需要你依据三条规则,确保索引键是一个好的标签。

SQL Server索引进阶:第六级,标签的更多相关文章

  1. SQL Server索引进阶:第五级,包含列

    原文地址: Stairway to SQL Server Indexes: Level 5, Included Columns 本文是SQL Server索引进阶系列(Stairway to SQL ...

  2. SQL Server索引进阶:第十级,索引内部结构

    原文地址: Stairway to SQL Server Indexes: Level 10,Index Internal Structure 本文是SQL Server索引进阶系列(Stairway ...

  3. SQL Server索引进阶:第三级,聚集索引

    原文地址: Stairway to SQL Server Indexes: Level 3, Clustered Indexes 本文是SQL Server索引进阶系列(Stairway to SQL ...

  4. SQL Server索引进阶:第二级,深入非聚集索引

    原文地址: Stairway to SQL Server Indexes: Level 2, Deeper into Nonclustered Indexes 本文是SQL Server索引进阶系列( ...

  5. SQL Server索引进阶:第一级,索引简介

    这个并不是我翻译的,全文共有15篇,但我发现好多网站已经不全,所以自己整理. 原文地址: Stairway to SQL Server Indexes: Level 1, Introduction t ...

  6. 【译】SQL Server索引进阶第八篇:唯一索引

    原文:[译]SQL Server索引进阶第八篇:唯一索引     索引设计是数据库设计中比较重要的一个环节,对数据库的性能其中至关重要的作用,但是索引的设计却又不是那么容易的事情,性能也不是那么轻易就 ...

  7. SQL Server索引进阶:第九级,读懂执行计划

    原文地址: Stairway to SQL Server Indexes: Level 9,Reading Query Plans 本文是SQL Server索引进阶系列(Stairway to SQ ...

  8. SQL Server索引进阶:第八级,唯一索引

    原文地址: Stairway to SQL Server Indexes: Level 8,Unique Indexes 本文是SQL Server索引进阶系列(Stairway to SQL Ser ...

  9. SQL Server索引进阶:第七级,过滤的索引

    原文地址: Stairway to SQL Server Indexes: Level 7,Filtered Indexes 本文是SQL Server索引进阶系列(Stairway to SQL S ...

随机推荐

  1. 02-4. BCD解密(10)

    BCD数是用一个字节来表达两位十进制的数,每四个比特表示一位.所以如果一个BCD数的十六进制是0x12,它表达的就是十进制的12.但是小明没学过BCD,把所有的BCD数都当作二进制数转换成十进制输出了 ...

  2. SQL Server 查看对象的权限

    例子 1. 查看登录名 loginA的权限: create login loginA with password = '123456'; go use studio; create user logi ...

  3. 快速美眉(FastMM)使用手记

    今天在SourceForge下到了FastMM (Fast Memory Manager),听说比官方的内存管理快多了,试了一下,果然不错.目前最新的是4.27. 就我的使用范围来说,我就是想看看我的 ...

  4. 04737_C++程序设计_第10章_面向对象设计实例

    10.6.2 使用包含的参考程序及运行结果. 头文件cpp10.h 源文件cpp10.cpp 源文件Find10.cpp 头文件cpp10.h #if ! defined(CPP10_H) #defi ...

  5. python 【第三篇】:函数及参数

    函数背景 在学习函数之前,一直遵循:面向过程编程: 根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下: ...

  6. 兔子--Android中的五大布局

    LinearLayout:被称为线性布局,分为水平和垂直,设置的垂直或水平的属性值,来排列全部的子元素.全部的子元素都被堆放在其他元素之后,因此一个垂直列表的每一行仅仅会有一个元素,而无论他们有多宽, ...

  7. Selector、shape详解,注意这两种图像资源都以XML方式存放在drawable不带分辨率的文件夹中

    Selector.shape详解(一) Selector的结构描述: <?xml version="1.0" encoding="utf-8"?> ...

  8. Linux 内核优化

    声明:本文档来自互联网整理部份加自已实验部份所得: TCP 相关部份   经常使用名词说明: retries(再试).   TCP server <---> client通信状态      ...

  9. javascript中数据类型转换

    转换为数字: parseInt():转换为整数型数值:从下标0开始判断,若为数值型则继续直到遇到非数值,返回前面的整数值: 小数点无效,若0开始为非数值则返回NaN: 转换空字符串会返回NaN: 能转 ...

  10. Javascript 基础编程练习一

    Javascript 基础互动编程,这篇练习结合了function 函数名(), onclick 时间, prompt输入窗口, window.open和confirm窗口, 任务 1.新窗口打开时弹 ...