汇集索引的叶子页存储的就是表的数据。所以,表行物理上按照汇集索引列排序,由于表数据只能有一种物理顺序,因此一个表只能有一个汇集索引。数据库
当咱们建立主键约束时,若是不存在汇集索引而且该索引没有被明确指定为非汇集索引,SQL Server会自动将其建立为惟一的汇集索引,这并非说主键列就必定是汇集索引,这只是默认行为而已。并发
示例,建表时经过指定主键为非汇集索引使主键列不为汇集列:post
CREATE TABLE MyTableKeyExample { Column1 int IDENTITY PRIMARY KEY NONCLUSTERED, Column2 int }
没有汇集索引的表称为堆表。堆表的数据列没有任何特别的顺序,链接到表的相邻页面。与访问大的汇集表相比,对标这种无组织的结构一般增大了访问大的堆表的开销。性能
有汇集索引的表称为汇集表,汇集表是B树结构,数据量大时,可以大幅减小读次数。字体
SQL Server中汇集索引和非汇集索引之间有一个有趣的关系,非汇集索引的一个索引列包含指向表的对应数据行的指针。这个指针被称为行定位器。行定位器的值取决于数据表是堆表仍是汇集表。当时堆表时,行定位器是指向堆中数据行的RID指针。对于具备汇集索引的表,行定位器是汇集索引键值。优化
下面用一个表格来讲明这种关系spa
假设有一个2列的表:3d
RID(这不是实际列) | 列1 | 列2 |
1 | A1 | A2 |
2 | B1 | B2 |
堆表: 指针
索引列(列1) | 行定位器 |
A1 | RID = 1 指向表中第一行数据 |
B1 | RID = 2 指向表中第二行数据 |
汇集表,假设咱们将列2设为汇集索引列:code
索引列(列1) | 行定位器 |
A1 | A2 指向汇集键 |
B1 | B2 指向汇集键 |
因而可知,经过非汇集索引列查找一行数据,还须要多一步-经过RID得到实际数据。这个RID在堆表是行指针,在汇集表是汇集键值。
一、首先建立汇集索引
对于汇集表而言,由于全部非汇集索引在其索引行上都保存一个汇集索引键值,因此非汇集索引和汇集索引建立的顺序很是重要。若是非汇集索引先于汇集索引建立,那么非汇集索引的行定位器将包含指向堆表的RID的指针。而后再建立汇集索引时,会将全部非汇集索引的RID指针改成汇集键,这实际上至关于从新创建了非汇集索引。
为了最好的性能,最好在建立任何非汇集索引以前建立汇集索引。这将使得非汇集索引在建立的时候将他们的行定位器直接设置为汇集索引值。这对最终的性能没有太大影响,可是SQL Server工做量少不少,速度快不少。若是你是在线上运行着的系统进行维护操做,这尤为有用。
二、保持窄索引
由于全部的非汇集索引将汇集索引键做为行定位器,为了最佳的性能,应使汇集索引的整体长度尽量小。
试想,假如建立了个宽的汇集索引,如CHAR(500),这将在每一个非汇集索引中添加一个500字节的值。就算非汇集索引什么都不放,光汇集索引键值占用的空间,它一页的数据页仅仅能存放16个数据行左右。
保持窄汇集索引能有效减小逻辑读操做与磁盘I/O。
三、一步重建汇集索引
由于汇集索引上有非汇集索引的依赖性,用单独的DROP INDEX 和 CREATE INDEX语句重建汇集索引将致使全部非汇集索引被重建两次(DROP,行定位器指向堆表数据行指针,CREATE行定位器指向新的汇集键值)。为了不这种状况,使用CREATE INDEX语句的DROP_EXISTING子句来在一个单独的原子步骤中重建汇集索引。类似地,也能够在非汇集索引上使用DROP_EXISTING子句。
CREATE CLUSTERED INDEX index1 ON PersonTenThousand(Id) WITH (DROP_EXISTING = ON)
四、什么时候使用汇集索引
在某些状况下,使用汇集索引是很是有帮助的。
一、检索必定范围的数据
由于汇集索引的叶子页面就是表的实际数据,汇集索引列的顺序就是表中数据行的物理顺序。若是数据行的物理顺序与查询请求的数据顺序相同,磁盘刺头能够顺序地读取全部行,而不须要太多的磁头移动。
假设我汇集索引创建在ID列,我须要读取ID BETWEEN 1 AND 100或ID > 100的数据,那么全部数据行在磁盘上排列在一块儿。这使磁头能够移动到磁盘上第一行的位置,而后用最少的磁头移动顺序读出全部数据。另外一方面,若是行在磁盘上没有以正确的物理顺序排列,磁头必须随机地从一个位置移动到另外一个位置来读取全部相关的行。磁头的物理移动是磁盘操做开销的最主要部分,将行以合适的物理顺序在磁盘上排序(使用汇集索引)优化了I/O开销。
二、读取预先排序的数据
汇集索引在数据读取须要排序时特别有效,若是在可能须要排序的一列或多列上建立一个汇集索引,那么行将被按该顺序物理排序,这消除了数据读取以后排序的开销。
在没有汇集索引的状况下,检索范围排序的数据:
在有汇集索引的状况下,检索范围排序的数据:
从中看到,有汇集索引的范围排序返回数据很是快速,由于对于汇集列,自己就是已经排好顺序存放于数据库中的。
五、什么时候不使用汇集索引
在某些状况下,最好不使用汇集索引。
一、频繁更新的列
若是汇集索引列频繁更新,将致使全部非汇集索引行的行定位器相应更新,从而显著地增长相关操做查询的开销。还将阻塞这段时间引用相同部分和非汇集索引的其余查询,从而影响数据库的并行性。所以,应该避免在大量更新的列上建立汇集索引。
二、宽的关键字
由于全部非汇集索引将汇集键做为其行定位器,因此为了性能,应该避免在很是宽或太多列上建立汇集索引。上面红色加粗字体特别说明了缘由。
三、太多并行的顺序插入
若是但愿并发地添加许多新行,那么对于性能来说,将他们分布到表的各个数据页面更好一些。可是,若是将全部行按照与汇集索引相同的顺序添加,那么全部的插入操做都在表的最后一个页面上进行。这可能在磁盘的对应山区形成一个巨大的“热点”,为了不磁盘热点,不该该将数据行按照物理位置相同的顺序排列。能够经过建立另外一列上的索引(该索引不会将行按照新航相同的顺序来排列)来插入操做随机地分布到整个表。这个问题只在大量同时插入时发生。
容许在表的尾部插入,可以避免须要容纳新行时发生的页拆分。若是并行插入数据下降,那么按照新行的顺序来排列数据行(使用汇集索引)将避免页拆分。可是,若是磁盘热点成为性能瓶颈,那么新行能够经过下降表的填充因子来容纳到中间页面。另外,“热”的页面将在内存中,这也有利于性能。
最后附上一个设置非主键为汇集索引列的方法:
1. 查看全部的索引,默认状况下主键上都会创建汇集索引
查看索引:
sp_helpindex person
查看约束:
sp_helpconstraint person
2. --删除主键约束,把【1】中查询出的主键上的索引约束【如:PK__person__117F9D94】去除掉。去掉主键字段上面的主键约束,此时该字段不是主键了。
alter table person drop constraint PK_Person
3.--建立汇集索引到其它列
create clustered index test_index on person(Name)
4.—修改原来的主键字段仍是为主键,此时会自动创建非汇集索引【由于已经有了汇集索引】
sp_helpindex person sp_helpconstraint person alter table person drop constraint PK_Person create clustered index test_index on person(Name) alter table person add primary key (id)
alter table person add primary key (id)