六、有关索引的几个问题
问题1,是否值得在identity字段上建立聚集索引。答案取决于identity 字段如何在语句中使用。如果你经常根据该字段搜索返回很少的行,那么在其上建立索引是值得的。反之如果identity字段根本很少在语句中使用,那么就不应该对其建立任何索引。
问题2,一个表应该建立多少索引合适。如果表的80%以上的语句都是读操作,那么索引可以多些。但是不要太多。特别是不要对那些更新频繁的表其建立很多的索引。很少表有超过5个以上的索引。过多的索引不但增加其占用的磁盘空间,也增加了SQL Server 维护索引的开销。
问题4:为什么SQL Server 在执行计划中没有使用你认为应该使用的索引?原因是多样的。一种原因是该语句返回的结果超过了表的20%数据,使得SQL Server 认为scan比seek更有效。
另一种原因可能是表字段的statistics过期了,不能准确反映数据的分布情况。你可以使用命令UPDATE STATISTICS tablename with FULLSCAN来更新它。只有同步的准确的statistics才能保证SQL Server 产生正确的执行计划。过时的老的statistics常会导致SQL Server生成不够优化的甚至愚蠢的执行计划。所以如果你的表频繁更新,而你又觉得和之相关的SQL语句运行缓慢,不妨试试UPDATE STATISTIC with FULLSCAN 语句。
你甚至可以使用Index hint比较不同索引的性能差异。比如对上面script 4提到的两个索引,可以这样比较,
select 学生姓名, 入学时间 from tbl1 with (index= idx_年龄入学时间)
where ……
或者:
select 学生姓名, 入学时间 from tbl1 with (index= idx_入学时间年龄)
where ……
如果强制使用你的索引后logical reads大大减少,那么就需要进一步研究为什么SQL Server 不使用正确的索引。注意,不要总是将使用索引等同于好的性能,反之亦然。SQL Server只在索引能提高性能时才使用索引检索。有时候使用Table scan的性能比使用某个索引反而要好。
问题5、什么使用聚集索引,什么时候使用非聚集索引
在SQL Server 中索引有聚集索引和非聚集索引两种。它们的主要差别是前者的索引叶子就是数据本身,而后者的叶子节点包含的是指向数据的书签(即数据行号或聚集索引的key)。
在上面的例子中我全部使用非聚集索引,原因是对一个表而言聚集索引只能有一个,而非聚集索引可以有多个。如果你把上面例子中的非聚集索引换成聚集索引,效果也是类似的,只是聚集索引没有Bookmark Lookup操作。什么时候应该使用聚集索引,什么时候使用非聚集索引取决于应用程序的访问模式。我的建议是在那些关键的字段上使用聚集索引。一个表一般都需要建立一个聚集索引。对于什么时候使用聚集索引,SQL Server 2000联机手册中有如下描述:
在创建聚集索引之前,应先了解您的数据是如何被访问的。可考虑将聚集索引用于:
- 包含大量非重复值的列。
- 使用下列运算符返回一个范围值的查询:BETWEEN、>、>=、< 和 <=。
- 被连续访问的列。
- 返回大型结果集的查询。
- 经常被使用联接或 GROUP BY 子句的查询访问的列;一般来说,这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引,可以使 SQL Server 不必对数据进行排序,因为这些行已经排序。这样可以提高查询性能。
- OLTP 类型的应用程序,这些程序要求进行非常快速的单行查找(一般通过主键)。应在主键上创建聚集索引。
聚集索引不适用于:
- 频繁更改的列
这将导致整行移动(因为 SQL Server 必须按物理顺序保留行中的数据值)。这一点要特别注意,因为在大数据量事务处理系统中数据是易失的。
- 宽键
来自聚集索引的键值由所有非聚集索引作为查找键使用,因此存储在每个非聚集索引的叶条目内。
七、结束语
如何使一个性能缓慢的系统运行更快更高效,不但需要整体分析数据库系统,找出系统的性能瓶颈,更需要优化数据库系统发出的SQL 语句。一旦找出关键的SQL 语句并加与优化,性能问题就会迎刃而解。读完本文,你应该知道创建索引的关键是什么,以及如何分析SQL语句的执行计划来创建索引。在优化了索引后大部分数据库系统的性能都能够得到不同程度的改善,有的系统甚至能够获得好几倍以上的性能提升。本文并不能解决你在优化SQL语句中碰到的所有问题,但其中讨论的内容或技巧对许多性能问题都有一定的参考意义。