索引是在表上创建的数据库对象,它可以提供到数据的更快的访问通道,并且可以使查询执行更快。索引为SQL Server提供了更有效地访问数据的方式。使用索引,你不用总去查找表中的每个数据页,检索特定行时也不用读取表的所有内容。
在默认情况下,常规的未作索引的表中的行不会以任何特定的顺序存储。处于无序状态的表被称为堆( heap)。为了基于一组搜索条件从堆中检索匹配的行,SQL Server不得不检查表中所有的行。即使只有一行匹配搜索条件,并且那行恰恰是SQL Server数据库引擎读取的第一行,SQL Server也仍然需要遍历每一条表记录,因为要知道是否有其他匹配行存在没有其他方法。这种信息扫描称为全表扫描。对于一个大型表,这可能意味着只是检索一行就要读取数百、数于、数百万或数十亿行。但是,如果SQL Server知道在表的一列或多列上存在索引,它就可能会使用这个索引更有效地去搜索匹配的记录。
在SQL Server中,表包含在一个或多个分区(partition)中。一个分区是一个组织单元,允许你水平划分表中和(或)索引中的数据,但仍然管理一个逻辑对象。当创建一个表时,默认情况下,它的所有数据都包含在一个分区中。分区包含了堆,或者当创建索引时,为B树结构(B-tree structure)。
当创建索引时,索引键数据存储在B树结构内。B树结构从索引开始的根节点开始。这个根节点(root node)拥有包含一系列指向下一级索引节点的索引键值的索引数据,称为中间叶级别(intermediate leaf level)。节点的底部级别称为叶级别(leaf Ievel)。叶级别基于实际的索引类型是聚集(clustered)或非聚集(nonclustered)而不同。如果它是聚集索引,则叶级别是实际的数据页本身。如果为非聚集索引,则叶级别包含指向堆或聚集索引数据页的指针。
聚集索引决定实际表数据如何进行物理存储。你只能指定一个聚集索引。这个索引类型依照指定的索引键列存储数据。聚集索引的B树结构如下图。注意叶级别是实际的数据页。
聚集索引的选择很关键,因为一个表只能拥有一个聚集索引。一般情况下,由于聚集索引的数据在物理上是以特定的顺序组织的,所以聚集索引经常选用以范围查询来查询的列。范围查询使用关键字BETWEEN以及大于(>)和小于(<)运算符。要考虑的其他列是那些用来排列大型结果集的列、用在聚合函数中的列以及包含完整唯一值的列。频繁更新的列以及非唯一列通常不是聚集索引键的好选择,因为聚集索引键包含在所有依赖的非聚集索引的叶级别上,这会引起额外的重新排序和修改。由于这个原因,应该避免在非常多或非常宽(许多字节的)的索引键上创建聚集索引。
非聚集索引存储在与物理数据分离的索引页上,指针指向位于索引页和节点的物理数据。非聚集索引列按照索引键列值的顺序进行存储。你可以在表上或索引视图上拥有最多249个非聚集索引。对于非聚集索引,叶节点级别是连接到指向堆的行或聚集索引行键的行定位器的索引键:
当选择用在非聚集索引的列时,要查找在WHERE、JOIN以及ORDER BY子句中频繁引用的那些列。搜索返回较小结果集的高选择性列(少于表中所有行的20%)。选择性是指唯一索引键值对应多少行。如果列是低选择性的,例如只包含0或1,SQL Server不可能在创建查询执行计划时利用该查询,因为它的选择性很低。
不管是聚集索引还是非聚集索引,都基于一个或多个键值。索引键是指用来定义索引本身的列。使用INCLUDE子句,可以让非键列加到索引的叶级别上,这是SQL Server 2005的新特性。这个新特性允许你的查询选择的更多列被返回或被单个非聚集索引“覆盖”,从而减少总的I/O,因为SQL Server根本不需要访问聚集叶级别数据页。
一个索引中最多可以使用16个键列,只要所有索引键列组合起来不超过900字节就可以。不可以在索引键中使用大值对象数据类型,包括varchar(max)、nvarchar(max)、varbinary(max)、xml、ntext、text以及image数据类型。
聚集或非聚集索引可以被指定为唯一的或非唯一的。选择唯一索引需要确保插入到键列的数据值是唯一的。对于使用多个键的唯一索引(称为组合索引),键值组合在表中每行都必须是唯一的。
正如之前提到的,索引为提高查询性能提供很大的帮助,但也会带来相应的代价。你应该只根据预期的奄询活动添加索引,并且必须持续地监视索引是否仍被使用。如果没有,应该将它们删除。在表上执行数据修改时,表上有太多的索引会引起性能开销,因为SQL Server除了数据的改变还必须维护索引的改变。正在进行的维护活动,比如索引重建和索引重组织,也会因为过多的索引造成延时。