列相同的索引(甚至好几列相同)。
b)第二种是从名字上看来可能是选择度很低的列,假设某个索引用了Gender(中国人大多用Sex)作为首列,我们知道性别通常只有两个值(最多三个:保密或…),这种列做索引的首列是很低效的,所以也应该列出来作为重点研究对象。
c)第三种有点难度,比较费精力,找出key_cols和Included_cols(包含列)中反复出现的列,这些索引可能对相同的列做索引,但是列的顺序不同而已,也需要研究是否有修改的必要。
?
例子演示:
如下面图中这两个索引,我们看到首列相同,并且索引列只有一列,但是第二个索引的包含列为NULL,经过下一步检查读写次数之后,基本上可以得知index_id=37的那个是否可以删除了。这种是典型的“重复索引”,可以归纳到索引过多的范畴。

?
下面来看看索引读写情况,为了减少篇幅,这里我们只查这两个索引的情况:

?
可以看到index_id=31的的索引的读的次数比37的索引接近6倍之多。如无意外index_id=37的索引是可以删除的,但是作为严谨考虑,我们需要再检查这两个索引的具体使用情况。其实经验表明,这种索引定义中,index_id=37的索引存在的唯一优点是“索引体积较小”,但是我们只要研究一下include列的数据类型就会知道会不会大很多,而且索引维护是一个权衡的过程,没有既提高读性能,又提高写性能的索引,这一点要时刻记住。
接下来就是抓索引被使用的对象情况。为了节省时间,我们可以用两个窗口,分别、同时获取两个索引的信息。下面是id=31的索引的信息:

?
对于database_name为null的数据,我们不必在意太多,毕竟这不是深入研究,脚本也并不是微软官方提供的,从图中可以看到这个索引被1708个对象使用过。
下图是id=37的索引的信息:

?
这个索引被772个对象使用了。
对比两个结果,id=37的索引前四行可以看看,后面的只使用了几次的索引不看也行。先点开第一个的执行计划看看,貌似有点多,那么看XML,并搜索索引被引用的地方:

?
整个执行计划里面就这个地方使用了这个索引,同时留意红框地方,UPDATE/Clustered Index Update。这是一个典型的“无用索引”,可以同时纳入“索引过多”和“索引不合理”的范畴。从这里看出,这个索引并没有实际被用到,仅仅因为UPDATE语句,触发了聚集索引更新,从而连带引发表上非聚集索引的更新。简单来说,它没有为性能带来好处,反而因为更新时多了这个索引,所以更新速度和开销更慢。并且我们回顾再上一个图,它被执行了2080825次,这种频率所带来的影响不可忽视。那么我门先标注一下:这个索引可以被删除。建议读者开一个excel,列出这些信息,并且包含删除、修改、保留等理由,也可以再加一列:删除可能性,每检查一个存储过程/SQL语句,如果可删除,那么加1,最后对比这个值是否最高,就可以判断是否优先删除。因为有些索引可能不合理,但是也不见得完全没用。这些索引是可以短时保留的。
用同样方法检查第二、第三个,情况一样,再检查第四个:

?
我们看到这次它被一个索引查找使用了,证明它可以协助查询,然后我们分析一下这个操作符,结合语句,发现它在查询中,WHERE条件的用到了这个索引的首列,同时输出列表中的列是主键,这里即聚集索引,而非聚集索引叶节点是包含了聚集索引键值,所以这个非聚集索引包含了WHERE和SELECT中所需的数据,所以优化器使用这个索引来协助查询。但是由于这个索引和id=31的那个几乎一样,所以我们完全可以认为这个索引是可以删除的,然优化器使用id=31的那个索引,只是查询所需的资源会略微增加而已。
使用同样的方法检查id=31的索引,以便验证我们的想法,这里就不一一截图。
通过这个方法,我们可以判断索引是否可以删除。并且如果你足够细心,可以在分析的过程中连带发现其他表的索引问题、语句是否合理等一些列的问题。当然,会很累。
上面的例子可以用于研究索引过多、索引不合理的情况。索引不合理主要是通过定义是否重复或者可合并、执行计划中是否出现了索引/聚集索引扫描或者其他需要注意的操作符(说明:每个操作符的出现有其原因和特定背景,并没有哪个操作符好,哪个不好。需要具体问题具体分析)。但是有些情况是很明显有问题的,比如:一个百万行的表,我只需要查询10条数据,并且SELECT中只用到少数几列,经过查询索引定义,某个索引包含了SELECT/WHERE/JOIN ON中的这些列。那么按道理来说,通常会进行查找操作,可以你在执行计划中发现它使用扫描操作。那么这就值得注意,这种情况通常是有问题的。常见的几种原因是:
?
统计信息过时:统计信息过时会导致优化器错误选择索引和索引的访问方式,可以通过上面查看索引定义语句中的stats_date列发现是否离现在很遥远。如果是,不妨更新一下统计信息。具体语句可以查看联机丛书的说明。索引定义不合理:上一篇文章已经演示过,如果首列定义错误,本来可以进行索引查找的操作会变成索引扫描。非SARG写法:如果WHERE条件中的列使用了标量函数、隐式转换等非SARG写法,也会导致“索引无效”。何为非SARG写法,这里不累赘,读者可以自行搜索。索引碎片过高:通常碎片问题会导致优化器不选择一个本来很好的索引,当索引碎片足够高时,假设表上只有这个索引可用,那么本来的查找操作会变成扫描操作。
?
建议:如果性能问题不是非常严重,在做完上面步骤后,可以先不删除索引,把信息记录到一个表,过一段时间后再重新操作一次,看看信息的变化情况。若确实不合理,那么把索引的定义保存起来,然后删了吧。
?
索引不足:
在三大索引问题中,现在已经解决了两个,剩下一个——索引不足。简单来说,这个问题主要就是找出哪些列需要建索引,为什么要建。但是建议这一步要在最后操作,因为索引过多和索引不合理的处理结果可能就是经过调整后,索引已经能满足性能要求,不需要再增加索引了。如果你把顺序反过来,那么可能在没有研究是否有多余索引之前,又加了一系列的索引,增加了研究的工作量和复杂度,从上面可以看出其实上面两步非常耗时。
这一步同样需要像上面那样收集同样的信息,所以建议用一些excel或者实体表存储过程信息。在个人经验中,查找哪些列需要加索引,有两类手段,建议同时使用:
1. SQL Server自带的缺少索引功能:从SQL 2005开始引入,但是2005的图形化执行计划并没显示缺少索引的提示。
2. 和上面步骤不一样,上面步骤研究的是非聚集索引,那么我们是否要研究一下聚集索引呢?因为没有索引的列,如果查询中使用到,那么除非是堆表,否则会访问聚集索引,所以从聚集索引的使用情况可以粗略得出哪些列需要索引化。
缺少索引:
缺少索引的文章也有很多,本人的书《SQLServer性能优化与管理的艺术》中也有描述,下面挑出一些重点来介绍。
缺少索引是有SQL Server在运行查询过程中,根据统计信息和索引情况记录在一系列DMO中的信息。这部分的DMOs是一些列的对象,由查询引擎在执行过程中收集的数据,当优化器编译一个执行计划是,会决定用什么索引及如何使用,如果索引不存在,会把这部分的信息存放到DMOs中。
这部分的DM