设为首页 加入收藏

TOP

SQLServer索引维护(1)――系统常见的索引问题(二)
2015-11-21 01:29:39 来源: 作者: 【 】 浏览:2
Tags:SQLServer 索引 维护 系统 常见 问题
稳定,但是过一段时间后又出现了。从表象看来,这类似与索引失效的情况,但是目前官方资料中并没有出现索引失效的描述,所以可以认为索引失效其实只是个表象,更多的是优化器对索引选择的混乱。本人将抽时间再起一个系列的文章专门讨论这部分内容。通过降低索引的数量和提高每个索引的重用度,这种情况目前再无发生。

简单来说,对于OLTP数据库,单表索引不应该过多。在这个快餐文化盛行的时代,很多人都希望直接得到标准值,而不去做实际分析,如果非要给出一个值,根据个人经验:

对于核心表:所有索引不要超过7个。对于普通表:所有索引不要超过5个。对于小型表:所有索引不要超过3个。

当单表有很多索引(比如上面提到的20多个)时,就应该考虑是否真有必要维持这种数量,在本系列的最后部分会给出解决方案。当然极端情况下,20多个索引的表性能表现也不一定不好。切记:具体情况具体分析。

综合前面两点,总结成一句:过犹不及!!

索引不合理:

其实上面两种情况,很多时候是因为索引设计不合理导致的。这里抛开聚集索引不说,对于非聚集索引,如果索引设计不合理,那么就没办法起到预期作用,大部分情况下表现为没有作用,这跟索引不足的结果是类似的。这时候很多人就会选择再建一个新的索引,如果对于索引的认识不足,可能建出来的新索引同样不能支持查询,导致再次创建索引,久而久之,又出现了索引过多的情况。

另外一种情况是,在中间接手的项目中,索引设计不合理,索引数量过多,那么很多人(包括我)就会选择“删”。当然既然我写这文章,就代表不会很暴力地删,我的原则是:建要有理有据,删也要有理有据,改同样也要有理有据。说不出理由,就别动它。因为存在,必然有理由,只是合理不合理而已。当删除索引之后,又可能出现了索引不足的情况,随之进入前面所说的一个循环过程中。为了避免思维混乱,对于上面的循环讨论就到此为止。下面来看看索引不合理的情况。

通常来说,索引不合理由于三种情况:

1.索引的列过多,当索引的列很多时,优化器可能会偏向于使用聚集索引,因为聚集索引本身存储了数据,而非聚集索引有时候需要二次定位,效率反而不好。同时虽然索引本身并不有序,但是聚集索引却能在某些情况下为查询排序提供帮助。另外,说白了,如果你的非聚集索引包含了大部分表的列,干嘛还要再建一个,直接用聚集索引好了。建非聚集索引很多情况下就是为了减少索引体积(因为通常列数更少),建立过多列的索引会得不偿失,哪怕性能有一点点提升,考虑到后续维护开销,也不建议这样做。

2.多列索引中,首列顺序不合理:有一定经验的SQL Server从业者应该知道索引的首列具有极其重要的地位。这要扯到统计信息,在这里暂时不详述。由于统计信息直接影响优化器在优化过程中对索引的选择,如果索引的首列使用了选择度很低(比如性别这类只有极少数不同值的列,选择度就很低)的列,那么有可能出现明明可以通过索引查找高效定位所需数据,却走了索引扫描,甚至优化器不用这个索引用其他非聚集索引或者聚集索引,使得这个索引形同虚设,失去了建立它的意义。关于这部分的讨论放到下面进行,总之,多列索引的首列,应该尽可能使用选择度高、数据类型尽可能“窄”、存储之后很少变化的列作为首列。

3.包含索引和覆盖索引:在SQL2005之前,索引只有两类,聚集索引和非聚集索引,但是从2005开始,非聚集索引就出现了很多变种,其中一个变种就是包含索引。它被广泛用于图形化执行计划中的缺少索引提示中。目前为止,本人见过的图形化执行计划中缺少索引的提示中,有且仅有包含索引,而没有其他任何类型的聚集索引和非聚集索引。因为包含索引的广泛使用,慢慢地,很多人已经忘记了其前身——覆盖索引:

a)覆盖索引指的是一个非聚集索引,这个索引包含了查询所需的所有列。

b)包含索引指的也是一个非聚集索引,但索引定义列(即在表名之后,在INCLUDE关键字之前的列集)中存储了表的一些列,而INCLUDE后面也定义了一些列。

c)两者最主要的区别在于包含索引中,出现在INCLUDE关键字后面的列(也称非键列)的值仅存储在非聚集索引的叶节点中,而覆盖索引中出现的所有列的键值都一直存储在每层节点中。从一定程度上,包含索引的体积更小,并且在INCLUDE后面的列顺序无关紧要,同时可以避免常规非聚集索引对16列,900bytes的固有限制。总的来说,包含索引有很多的优势。

但是恰恰因为这些优点,很多人建索引的时候都不管三七二十一,全用包含索引。在这里提出这种说法,显然是表示不能滥用。因为包含索引相对于覆盖索引而言,有以下几个不足:

a)移植性:虽然几率微乎其微,但是万一需要把数据库降级回2000,包含索引会报错。这里就引出另外一个编程规范的问题:T-SQL并不完全遵循标准SQL,其有自己独特的地方,对于一些写法,如果都能满足需求,那么优先选择标准写法而不是T-SQL自己的写法,这样也具有更好的移植性。如<>和!=,前者是标准语法,后者不是。此时建议使用<>来表达“不等于”。

b)覆盖查询的功能:使用AdventureWorks2008R2示例数据库,使用下面代码

1. 创建演示环境:

use AdventureWorks2008R2
go
--为避免影响演示环境,创建一个新表dbo.Person用于测试
select * into dbo.Person
from Person.Person
GO
--参照原表创建主键:
ALTER TABLE dbo.Person ADD CONSTRAINT
    PK_Person PRIMARY KEY CLUSTERED
    (
    BusinessEntityID
    )
GO 

2.编写查询:先不建任何索引,通过实际执行计划可以看到只走聚集索引

--写一个演示查询,由于一开始没有非聚集索引,所以查询会使用聚集索引
select Title,FirstName,MiddleName,LastName
from dbo.Person
where FirstName like 'o%'

\

?

3.添加一个覆盖索引,覆盖查询中WHERE条件和SELECT条件的列

--这里为了避免涉及过多内容,对索引首列做了限制,用于满足where条件
CREATE NONCLUSTERED INDEX IX_Person_FirstName
ON dbo.Person(FirstName,Title,MiddleName,LastName)
GO
 
--添加一个覆盖索引,覆盖查询中WHERE条件和SELECT条件的列
select Title,FirstName,MiddleName,LastName
from dbo.Person
where FirstName like 'o%'

从下图可见,索引可以覆盖我们的查询:

\

?

4.删除上面的索引,添加一个包含索引,但是WHERE条件中的FirstName列只出现在INCLUDE中:

?

--创建包含索引
CREATE NONCLUSTERED INDEX IX_Person_FirstName
ON dbo.Person(Title) INCLUDE(FirstName,MiddleName,LastName)
GO
 
--删除上面的索引,添加一个包含索引,但是WHER
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇SQL Server 利用锁提示优化Row_nu.. 下一篇SQL中删除同一字段中重复的值

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: