在SQL Server中,非聚集索引其实可以看做是一个含有聚集索引的表,但相比实际的表而言,非聚集索引中所存储的表的列数窄很多,因为非聚集索引仅仅包含原表中非聚集索引的列和指向实际物理表的指针。
并且,对于非聚集索引表来说,其中所存放的的列是按照聚集索引来存放的,所以查找速度要快了很多。但是对于性能的榨取来说,SQL Server总是竭尽所能,假如仅仅是通过索引就可以在B树的叶子节点上获取所需数据,而不再通过叶子节点上的指针去查找实际的物理表,那性能的提升将会更胜一筹。
非聚集索引的覆盖
正如前面简洁所说。非聚集索引其实可以看作一个聚集索引表,当这个非聚集索引中包含了查询所需要的所有信息时,则查询不再需要去查询基本表,而仅仅是从非聚集索引就能得到数据:
下面来看非聚集索引如何覆盖的:
在adventureWorks中的SalesOrderHeader表中,现在只有CustomerID列有非聚集索引,而BillToAddressID没有索引,我们的查询计划是这样的:
查询会根据CustomerID列上的非聚集索引找到相应的指针后,去基本表上查找数据.从执行计划可以想象,这个效率并不快。
下面我们来看覆盖索引,通过在CustomerID和BillToAddressID上建立非聚集索引,我们覆盖到了上面查询语句的所有数据:
通过覆盖索引,可以看到执行计划简单到不能再简单,直接从非聚集索引的叶子节点提取到数据,无需再查找基本表!
这个性能的提升可以从IO统计看出来,下面我们来看有覆盖索引和没有覆盖索引的IO对比:
因为减少了对基本表的依赖,所以提升了并发,从而减少了死锁!
理解include的魔力
上面的索引覆盖带来的效率提升就像魔术一样,但这要付出代价的,如果一个所以包含了太多的键的话,也会带来很多副作用,include的作用使得非聚集索引中可以包含更多的列,但不作为“键”使用。
比如:假设我们上面的那个查询需要增加一列,则原来建立的索引无法进行覆盖,从而还需要查找基本表:
但是如果包含SubTotal这个总金额,则索引显得太宽,因为我们的业务很少根据订单价格作为查询,则使用include建立索引:
理解INCLUDE包含的列和索引建立的列可以这样理解,把上述建立的含有INCLUDE的非聚集索引想像成:
使用INCLUDE可以减少叶子“键”的大小!
非聚集索引的交叉
非聚集索引的交叉可以看做是覆盖索引的扩展,由于很多原因,比如:
1、在生产环境中,我们往往不能像上面建立覆盖索引那样随意改动现有索引,这可能导致的结果是你会更频繁的被客户打电话“关照”
2、现有的非聚集索引已经很“宽”,你如果继续拓展则增改查带来的性能下降的成本会高过提高查询带来好处
这时候,你可以通过额外建立索引。正如我前面提到的,非聚集索引的本质是表,通过额外建立表使得几个非聚集索引之间进行表一样的join,从而非聚集索引之间可以进行join来在不访问基本表的情况下给查询优化器提供所需要的数据: