| 主存储格式 | 索引类型 |
|---|---|
| 基于磁盘的行存储 | 聚集、非聚集、唯一、Filtered |
| 列存储 | 聚集列存储、非聚集列存储 |
| 内存优化 | 哈希、内存优化非聚集 |
行存储是存储关系表数据的传统方法。 “行存储”是指基础数据存储格式为堆、B+ 树(聚集索引)或内存优化表的表。 “基于磁盘的行存储”排除了内存优化表。
一般给表中的某个字段添加索引,使用的是基于磁盘的行存储索引。
对于行存储索引,这些键以树结构(B+ 树)存储。
它是按顺序排列的值列表,每个值都有指向这些值所在的数据页面的指针。
索引本身存储在页上,称为索引页。
对表编制大量索引会影响 INSERT、UPDATE、DELETE 和 MERGE 语句的性能,因为当表中的数据更改时,所有索引都须适当调整。 例如,如果在多个索引中使用了某个列,并且执行了修改该列数据的 UPDATE 语句,则必须更新包含该列的每个索引以及基础的基表(堆或聚集索引)中的该列。
避免对经常更新的表进行过多的索引,并且索引应保持较窄,就是说,列要尽可能少。使用多个索引可以提高更新少而数据量大的查询的性能。 大量索引可以提高不修改数据的查询(例如 SELECT 语句)的性能,因为查询优化器有更多的索引可供选择,从而可以确定最快的访问方法。对小表进行索引可能不会产生优化效果,因为查询优化器在遍历用于搜索数据的索引时,花费的时间可能比执行简单的表扫描还长。 因此,小表的索引可能从来不用,但仍必须在表中的数据更改时进行维护。
对于聚集索引,请保持较短的索引键长度。 另外,对唯一列或非空列创建聚集索引可以使聚集索引获益。
无法指定 ntext、 text、 image、 varchar(max) 、 nvarchar(max)、
varbinary(max) 数据类型的列为索引键列。
在列中检查数据分布。 通常情况下,为包含很少唯一值的列创建索引或在这样的列上执行联接将导致长时间运行的查询。例如:‘性别’只有男和女两种
如果索引包含多个列,则应考虑列的顺序。 在 WHERE 子句中使用的列等于 (=) ,大于 () ,小于 < (>) ,或 BETWEEN 搜索条件或参与联接,应首先放置。
考虑对计算列进行索引。
行存储索引是按 B+ 树结构组织的。 索引 B+ 树中的每一页称为一个索引节点。 B+ 树的顶端节点称为根节点。 索引中的底层节点称为叶节点。 根节点与叶节点之间的任何索引级别统称为中间级。 在聚集索引中,叶节点包含基础表的数据页。 根节点和中间级节点包含存有索引行的索引页。 每个索引行包含一个键值和一个指针,该指针指向 B+ 树上的某一中间级页或叶级索引中的某个数据行。 每级索引中的页均被链接在双向链接列表中。
聚集索引在 sys.partitions中有一行,其中,索引使用的每个分区的 index_id = 1。 默认情况下,聚集索引有单个分区。 当聚集索引有多个分区时,每个分区都有一个包含该特定分区相关数据的 B+ 树结构。 例如,如果聚集索引有四个分区,就有四个 B+ 树结构,每个分区中有一个 B+ 树结构。
根据聚集索引中的数据类型,每个聚集索引结构将有一个或多个分配单元,将在这些单元中存储和管理特定分区的相关数据。 每个聚集索引的每个分区中至少有一个 IN_ROW_DATA 分配单元。 如果聚集索引包含大型对象 (LOB) 列,则它的每个分区中还会有一个 LOB_DATA 分配单元。 如果聚集索引包含的变量长度列超过 8,060 字节的行大小限制,则它的每个分区中还会有一个 ROW_OVERFLOW_DATA 分配单元。
数据链内的页和行将按聚集索引键值进行排序。 所有插入操作都在所插入行中的键值与现有行中的排序顺序相匹配时执行。
下图显式了聚集索引单个分区中的结构。
使用运算符(如 BETWEEN,、>>=、<和 <=)返回值范围。
使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行物理相邻。 例如,如果某个查询在一系列销售订单号间检索记录, SalesOrderNumber 列的聚集索引可快速定位包含起始销售订单号的行,然后检索表中所有连续的行,直到检索到最后的销售订单号。
返回大型结果集。
使用 JOIN 子句;一般情况下,使用该子句的是外键列。
使用 ORDER BY 或 GROUP BY 子句。
在 ORDER BY 或 GROUP BY 子句中指定的列的索引,可以使 数据库引擎 不必对数据进行排序,因为这些行已经排序。 这会有助于提升查询性能。
一般情况下,定义聚集索引键时使用的列越少越好。 考虑具有下列一个或多个属性的列:
唯一或包含许多不重复的值。
例如,雇员 ID 唯一地标识雇员。 EmployeeID 列的聚集索引或主键约束可提高基于雇员 ID 号搜索雇员信息的查询的性能。 另外,可对 LastName、 FirstName、 MiddleName 列创建聚集索引,因为经常以这种方式分组和查询雇员记录,而且这些列的组合还可提供高区分度。
提示:如果没有另行指定,在创建主键约束时,数据库引擎会创建一个聚集索引来支持该约束。 虽然可使用 uniqueidentifier 来强制实施作为主键的唯一性,但它不是有效的聚集键。 如果使用 uniqueidentifier 作为主键,建议将其创建为非聚集索引,然后使用另一列(如 IDENTITY)创建聚集索引。
按顺序被访问
例如,产品 ID 唯一地标识 Production.Product 数据库的 AdventureWorks2019 表中的产品。 在其中指定顺序搜索的查询(如 WHERE ProductID BETWEEN 980 and 999)将从 ProductID的聚集索引受益。 这是因为行将按该键列的排序顺序存储。
定义为 IDENTITY。
经常用于对表中检索到的数据进行排序。
频繁更改的列
这将导致整行移动,因为 数据库引擎 必须按物理顺序保留行中的数据值。 这一点要特别注意,因为在大容量事务处理系统中数据通常是可变的。
宽键
宽键是若干列或若干大型列的组合。 所有非聚集索引将聚集索引中的键值用作查找键。 为同一表定义的任何非聚集索引都将增大许多,这是因为非聚集索引项包含聚集键,同时也包含为此非聚集索引定义的键列。
基于磁盘的行存储非聚集索引包含索引键值和指向表数据存储位置的行定位器。 可以对表或索引视图创建多个非聚集索引。 通常,设计非聚集索引是为改善经常使用的、没有建立聚集索引的查询的性能。
与使用书中索引的方式相似,查询优化器在搜索数据值时,先搜索非聚集索引以找到数据值在表中的位置,然后直接从该位置检索数据。 这使非聚集索引成为完全匹配查询的最佳选择,因为索引包含说明查询所搜索的数据值在表中的精确位置的项。 例如,为了从 HumanResources. Employee 表中查询向特定经理负责的所有雇员,查询优化器可能使用非聚集索引 IX_Employee_ManagerID;它以 ManagerID 作为其键列。 查询优化器能快速找出索引中与指定 ManagerID匹配的所有项。 每个索引项都指向表或聚集索引中准确的页和行,其中可以找到相应的数据。 在查询优化器在索引中找到所有项之后,它可以直接转到准确的页和行进行数据检索。
基于磁盘的行存储非聚集索引与聚集索引具有相同的 B+ 树结构,它们之间的显著差别在于以下两点: