Heap 就是数据页之间没有顺序以及数据页中数据行之间没有顺序的表,是最简单的表结构,数据行被加到数据页第一个可用位置,如果当前页没有足够空间的话,就创建额外的数据页; SQL Server 在称为 Index Allocation Map 的内部结构中使用一个条目跟踪可用的数据页,这个条目的 index_id=0
在堆上执行的操作:
INSERT :每个新行都放在第一个可用页上
UPDATE :数据行即可以仍然在同一页上(如果大小合适的话),或者被从当前页删除,并放到第一个可用页上
DELETE :释放当前页的空间,数据没有被覆盖,空间仅仅是被标记为可以被重用(即数据不会立即被删除,只有在需要在这块被删除的空间上分配新数据时,才会擦除被删除的数据)
SELECT :如果没有索引的话,需要读取整张表
forwarding pointers就是在数据行被移动时,在原来的位置留下来一个引用,这个引用指示数据行被移动到哪个位置,这样就不需要更新其他指向这行的索引(这些索引还是指向这个位置,而这个位置告诉这些索引数据行的位置),当数据行再次移动的时候,更新这个引用指向数据行新的位置(而不是在新的位置在留下一个引用,指向数据行这次被移到了什么位置,这样查询性能不会引用跟随forwarding pointers链而下降),forwarding pointers随着时间的推移会带来性能问题
可以通过移除forwarding pointers并更新其他索引来提高性能,在 SQL Server2008 之前没有非常简单的方式,因为之前只能重建索引,不能重建表, SQL Server2008 引入了重建表能力( ALTER TABLE WITH REBUILD )
表建立聚集索引后,表中数据页、数据页上的行都有确定的顺序,每个表只能建一个聚集索引(这个索引可以一个或者多个列上创建),在 Index Allocation Map 中创建一个条目指向聚集索引,这个条目的 index_id=1 ;通常都误认为使用聚集索引的数据页物理上按顺序存储的,实际上很少是这样,数据页之间也是双向链接的
聚集索引上的操作:
INSERT :每个新行都放在正确的逻辑位置,可能会涉及到拆分数据页
UPDATE :如果数据合适并且聚集键没有被修改的话,数据行还保留在原来的位置;如果数据行不在合适的话,就拆分数据页;如果聚集键修改的话,移除数据行,并放在正确的逻辑位置上
DELETE :释放空间,并将数据标记未使用
SELECT :和聚集键相关的查询可以直接找到或者扫描,避免排序
如果聚集索引没有指定为 unique , SQL Server 将向聚集键添加值保证每行是唯一的,该值称为 uniqueifier ;如果索引是唯一的话,应该总是将它们指定为 unique 的
可以通过 CREATE CLUSTERED INDEX 或者指定 PRIMARY KEY 的方式创建聚集索引;注意 PRIMARY KEY 和聚集索引的区别: PRIMARY KEY 是约束,是索引支持的逻辑概念,而索引可以是聚集索引也可以是非聚集索引,也就是说创建主键,一定创建了索引,但可能是非聚集索引,不是必须是聚集索引,默认创建主键的时候,如果表没有聚集索引的话,就在主键列上建立聚集索引,可以通过 NONCLUSTERED 关键修改默认行为,使其创建非聚集索引
通过 DROP INDEX 语句删除聚集索引;对于内部支持约束的索引,需要通过删除约束来删除索引,比如使用主键作为索引的需要使用 ALTER TABLE 语句将主键删除来删除聚集索引;删除聚集索引后,数据不会丢失,表被在堆中重新组织
ALTER INDEX 只可以执行次要的操作,比如 rebuilt 或者 reorganized (只是重新组织索引叶子节点的 level ),不允许 restructuring 索引,即不允许添加或者移除组成索引键的数据列,并且不能将索引移到另外一个 FileGroup 中;可以通过 CREATE INDEX...WITH DROP EXISTING 改变索引的结构,这个命令是替换原有的索引,这个命名不能将聚集索引修改为非聚集索引,反之亦然;可以通过在 ALTER INDEX 语句中包含 DISABLE 选项禁用索引,一旦禁用索引不允许访问表中的数据,直到 rebuilt
索引页中的 Free Space 可以显著提高索引更新性能,因为如果插入索引时没有足够的空间时,需要新索引页,需要拆分原有索引页,但是 Free Space 损害了查询的性能, FILLFACTOR 指定页面填充率( 默认是 0 ,表示 100% 占满,如果指定 70 的话,表示填充 70% ,剩余 30% ), FILLFACTOR 仅仅应用于叶子节点,如果启用了 PAD_INDEX 的话,则也应用于非叶子节点
好的聚集索引键的特征:
短,索引键必须限制在 16 列, 900 字节,好的索引键要比这短的多
静态,索引键基于的数据不改变,这就是主键经常作为聚集索引键的原因之一,因为对索引键的修改需要移动数据行
Increasing ,键值不断增加,帮助插入,这样新插入的数据都放在表尾部,最小化索引碎片、数据页拆分、减少页缓存需要的内存空间
唯一,这样不需要添加 uniqueifier ,这也说明将值是唯一的列声明为 unique 的重要性
int 和 bitint 是最好的聚集键,尤其是和 IDENTITY 约束一起使用的时候; uniqueidentifier( 即 GUID) 作为聚集键的问题是他们比 int 类型 size 要大,更主要的是他们的值是随机的不连续,会造成许多索引碎片;字符类型作为聚集键的问题是他们排序性能不佳,而且在应用程序中他们的值经常改变;日期类型大小、排序性能都非常好,问题是不唯一,经常用来进行日期范围查询
可以在视图上创建聚集索引,称为 indexed view ,其他数据库称为 materialized view ,这种类型的视图已经被物化,即不再是存储一个 SELECT 语句声明,而是存储实际的数据;对于维护提前计算好的聚合值和 join 来说, indexed view 非常有用; indexed view 就是一种建立了聚集索引的特殊类型的表,该表不是通过通常的方式建立的,而是通过 SELECT 语句建立的,当底层表数据修改时, indexed view 中的数据也会被修改;可以显著提高查询性能,但是会降低增删改的性能,对于经常查询很少修改的数据非常有用
为了在查询中使用 indexed view ,需要在创建视图和使用视图时,设置一系列的 SET 选项,尤其是 CONCAT_NULL_YIELDS_NULL 和 QUOTED_IDENTIFIER 设置;为了视图上建立索引,视图必须是确定的,即除非底层表进行修改,否则视图必须总是返回相同的数据,比如不能包含使用 SYSDATETIME 函数的列;创建视图时,必须指定 SCHEMABINDING 选项,该选项阻止底层表在视图还存在的情况下修改自己架构; Enterprise 和 Data Center 版本自动利用 indexed views 特性,而其他版本则需要 NOEXPAND 查询提示
相关文章: