引是数据库规划和系统维护中至关重要的部分。它们为SQL Server(就此而言,还有其他数据库系统)提供另外的查找数据的手段,并通过捷径抵达数据的物理位置。添加正确的索引能够极大减少查询的执行时间。遗憾的是,许多的设计糟糕的查询实际上会增加执行查询的时间。事实上,索引是SQL Server提供的对象中最容易误解的对象之一,因而也倾向于成为最容易处理失当的对象之一。
本章将既从开发者又从管理员的视角来深入学习索引。不过,为了理解索引,还必须了解数据是如何在数据库中存储的。鉴于此,本章中也要讨论SQL Server的数据存储机制,包括SQL Server使用的索引分配策略以及内在结构。
8.1 SQL Server存储
可以把SQL Server中的数据想成是存储在某种层次结构中的数据。这种层次非常简单。层次中的一些对象是你将直接处理的,因而很容易了解。许多其他的对象则存在于保护之下,虽然在一些情况下能够直接处理它们,但通常是不行的。下面来逐一讲解。
8.1.1 数据库
数据库是比较容易的概念。有读者或许会说着“我已经都知道了。”是的,很可能大家都知道,但是,由于它是存储定义的最高级别(对于一个指定的服务器而言),因而这里作为一个独立的实体指出它。数据库是能够建立锁的最高级别,不过无法显式地创建数据库级别的锁。
锁是系统使用的某种控制和位置标记。第12章将全面讲述锁,但是,当我们讲述存储时,会顺便讨论到SQL Server中对象的可锁定性。
8.1.2 文件
默认情况下,与数据库有关的文件有两种:
l 第一种是主物理数据库文件——它是数据最终存储的地方。应当以*.mdf为扩展名命名这种文件(这是推荐的方法,并不是必须如此——不过,你会发现以别的方式命名将随着时间的推移而变得让人迷惑)。
l 第二种是数据库文件的某种分支——日志。在第12章中讲述事务和锁时,会深入研究日志,这里应该明白日志保存在于它自己的文件中(应当以*.ldf为扩展名)。并且,没有了它数据库将无法工作。日志是从最后一次把数据“提交”到数据库中以来,发生在数据库上的事情的连续记录。实际上,数据库不是完整的数据集。日志不是完整的数据集。只有从数据库入手,并“应用”(从中添加所有的活动)日志,才能拥有完整的数据集。
对于这些文件相互之间如何放置没什么限制。可以把每一个文件放在单独的物理设备上(实际上,甚至很需要这样做)。这不仅允许一个文件中的活动不会影响到另一个文件中的活动,而且还创造出这样的情形:当数据库文件受损时,不会导致损失所做的工作——可以恢复备份然后重新应用日志(它安全地存放在另一个驱动器上)。同样,如果损失的是存放日志的驱动器,仍然能获得到最近的检查点时间的有效的数据库(在关于锁和事务的章节中将全面讲述检查点)。
8.1.3 区段
区段(extent)是用来为表和索引分配空间的基本存储单元。它由8个连续的数据页组成(64K)。
基于区段而不是实际使用的空间来分配空间的概念,对习惯于操作系统存储原则的人来说,多少有点难以理解。关于区段的要点包括:
l 一旦一个区段已满,下一条记录将占用一个完整的新区段的大小,而不是记录本身的大小。许多新接触SQL Server的人在空间估算上失误,部分是由于一次分配的是一个区段而非一条记录的大小。
l 通过预先分配空间,SQL Server省下了为每一条记录分配新空间的时间。
仅仅因为要添加的行比现在分配的区段所能容纳的行多了一行,就要另外占用整个区段,这似乎是一种浪费。但是,以这种方式浪费的空间总量通常不那么多。然而,它们会积少成多(特别是在高度碎片化的环境中),因此你一定要紧记。
占用整个区段空间的好处是SQL Server省去了一些分配空间的时间上的开销。SQL Server不必每写入一行就考虑分配问题,而是在需要新的区时才处理额外空间的分配。
别把区段所占用的空间与数据库占用的空间混为一谈。分配给数据库的空间是从硬盘可用空间中消失的空间。区段的空间则仅仅是在数据库获得的整个空间中分配的空间。
8.1.4 页
|
与区是数据库中的分配单元类似,页是特定区段中的分配单元。每一个区段有8个页。
|
|
|
|
|
有很多种不同的页类型。鉴于本书的目的,这里关心如下的类型:
l 数据;
l 索引;
l 二进制大型对象(BLOB)(用于Image以及大多数的Text和Ntext数据);
l 全局分配映射表(GAM)和共享全局分配映射表(SGAM);
l 页可用空间(PFS);
l 索引分配映射(IAM);
l 大容量更改映射表;
l 差异更改映射表。
1.数据页
数据页是不言自明的——它们是表中实际的数据,除了所有没有使用text in row选项定义的BLOB数据之外。当行中有一个列包含BLOB数据时,常规的数据存储在数据页中——通常有一个16字节的指针指向BLOB页,该页包含构成BLOB的二进制信息。当使用了text in row选项时,如果文本短到能够放入可用的页面空间中,则把文本与数据存储在一起(否则,依然会使用16字节指针)。
2.索引页
索引页也非常简单明了:它们保存非聚集索引的非叶级页和叶级页(在本章的后面将考查它们是什么),以及聚集索引的非叶级页。当继续本章的学习时,将明了这些索引类型。
3.BLOB页
BLOB页用于存储二进制大型对象。对于SQL Server来说,它们是所有存储在Image字段中的数据以及绝大多数的Text和Ntext数据。由于BLOB页中没有任何行,就数据存储页而言BLOB页是特殊的。既然BLOB的大小能够达到2GB,因而它们必须多于一个页——就这部分,说法是什么并不重要。为了存储整个BLOB,SQL Server将分配它所需的足够多的页,但不保证这些页是连续的——页可以位于数据库文件中的任何地方。
正如前面说过的,在行中的非blob数据和与该行有关的BLOB数据之间,以指针的形式联系在一起。在SQL Server的7.0版本中,指针的性质以及SQL Server如何导航到BLOB数据的方式发生了改变。在6.5版本及以前,BLOB页以链的形式放在一起——类似于链表。要找到任何是BLOB的一部分的页,必须从链表的起始部分开始,一页一页地在BLOB中导航。如果试图进行某种文本或二进制形式的搜索,这种排列将是致命的,因为不得不陷入数据的串行扫描中。然而,从7.0版开始有了改变,页被组织为了B树结构(本章稍后将对此做充分的讨论)。B树提供了一种有更多分支的结构,因此,为更大型的BLOB提供了更直达的路线。这在文本操作能够执行得多快上产生了很大的不同。
即使BLOB的执行性能在7.0版中有了显著提高,它依然很慢,因此,当后面讲述高级的设计问题时,将讨论另一种存储方法。
4.全局分配映射表、共享全局分配映射表和页可用空间页
全局分配映射表(GAM)、共享全局分配映射表(SGAM)和页可用空间(PFS)页类型涉及确定哪些区和页在使用、哪些没在使用。本质上,这些页存储的是说明哪些空间可用的记录。其实,完成高质量的开发或系统管理并非一定要理解这些页的类型,而且,这些内容已经超越了本书的讲述范围。尽管如此,如果你非常想了解它们,可以在联机丛书中找到更多关于它们的信息——在索引中查找GAM即可。
5.大容量更改映射表
由于现在还没有讨论过大容量操作,我们该怎么讲述这一主题呢……
SQL Server中有“大容量操作”的概念。大容量操作是对数据库非常快速的更改(通常是大规模的数据导入或截断表)。这样的速度部分来自于大容量操作不“记录”它们所做的每一件事情。日志对于备份和恢复系统是至关重要的,而大容量操作意味着数据库中发生了没有记入日志的活动(它会记录进行了某些操作,但不记录操作的详情,因而日志无法重建所做的事情)。
大容量更改映射表(BCM)是一组用于跟踪哪些区通过大容量操作进行了修改的页。它对于修改的细节不感兴趣——只关心你对特定的区作了改动。由于知道该区发生了改变,在备份数据库时,它能提供更多的选择。更明确地说,当进行日志备份时,能够对受大容量操作影响的区上的物理数据进行备份,以此作为对日志备份的补充。
6.差异更改映射表
它与大容量更改映射表几乎一样,只不过,它不是只跟踪被大容量操作更改的区,它跟踪自最后一次备份数据库以来发生了更改的所有区。
当请求进行差异备份时,差异更改映射表(DCM)提供什么区需要备份的信息。由于备份只包含自上一次备份以来发生过改动的区,因此该备份更小且速度更快(虽然只是部分的)。
7.页拆分
页满时,会对其做拆分。这意味着不仅要分配一个新页,而且,要把现有页上大约一半的数据移动到新页上。
关于这一过程的例外情况是使用了聚集索引的时候。如果存在聚集索引,并且下一个插入的行物理上位于表的最后一行,那么将创建一个新页,并把新行添加到新的页上,而不会重新部署现有的数据。在研究索引时,会更深入地讨论页拆分。
8.1.5 行
鉴于你将听到大量关于“行级锁”的说法,因此,对于行这一术语应该不会太意外。通常,行的大小能够达到8KB。
除了行的大小限制为8060字符外,行中的最大列数限制为1024列。实际处理中,在列的大小达到8060字符的限制之前,极少发生列的数目不够用的情况。当为1024列时,平均列的大小为8字节。多数应用中会超出这一大小。关于这一点的例外是在测量和统计信息中——要存储大量不同的数字样本。尽管如此,即便是在这样的应用中,达到1024列的限制也是很罕见的。
你可能已经注意到,在提及8KB的限制时,我使用了“通常”一词。该限制是基于一行限制在一页中,且页的大小为8KB,因而有此限制。但是,在一些情况下可以超出这样的限制——具体地说,是在使用varchar(max)或varbinary(max)以及传统的BLOB数据类型如image和text时。如果行在这些数据类型中的一种中包含的数据过多,无法放置在一页里,那么,这些特殊的数据类型知道如何让数据跨越多个页(一行可以达到2GB)。在这种情况下,原始的行用来记录该列中实际的数据存放在何处(所有其他的列依然存放在原始的行中)。
8.1.6 全文目录
这是位于标准的数据库之外单独的存储机制。虽然能够把全文目录默认关联到指定的数据库上,并且从SQL Server 2005开始,甚至能够与数据库一起备份全文目录,全文目录依然是单独存放的。全文目录的内部结构非常不同,而且,其存储也是由完全不同的服务所管理的(将在第21章中讨论与全文有关的内容)。






