原文地址:http://tech.it168.com/a2009/1125/814/000000814758_all.shtmlhtml
实施计算列并在这些列上建立索引数据库
你可能曾经写过从数据库查询一个结果集的应用程序代码,对结果集中每一行进行计算生成最终显示输出的信息。例如,你可能有一个查询从数据库检索订单信息,在应用程序代码中你可能已经经过对产品和销售量执行算术操做计算出了总的订单价格,但为何你不在数据库中执行这些操做呢?后端
请看下面这张图,你能够经过指定一个公式将一个数据库表列做为计算列,你的TSQL在查询清单中包括这个计算列,SQL引擎将会应用这个公式计算出这一列的值,在执行查询时,数据库引擎将会计算订单总价,并为计算列返回结果。性能优化
图 1 计算列函数
使用计算列你能够将计算工做所有交给后端执行,但若是表的行数太多可能计算性能也不高,若是计算列出如今Select查询的where子句中状况会更糟,在这种状况下,为了匹配where子句指定的值,数据库引擎不得不计算表中全部行中计算列的值,这是一个低效的过程,由于它老是须要全表扫描或全汇集索引扫描。性能
所以问题就来了,如何提升计算列的性能呢?解决办法是在计算列上建立索引,当计算列上有索引后,SQL Server会提早计算结果,而后在结果之上构建索引。此外,当对应列(计算列依赖的列)的值更新时,计算列上的索引值也会更新。所以,在执行查询时,数据库引擎不会为结果集中的每一行都执行一次计算公式,相反,经过索引可直接得到计算列预先计算出的值,所以在计算列上建立一个索引将会加快查询速度。优化
提示:若是你想在计算列上建立索引,必须确保计算列上的公式不能包括任何“非肯定的”函数,例如getdate()就是一个非肯定的函数,由于每次调用它,它返回的值都是不同的。设计
建立索引视图代理
你是否知道能够在视图上建立索引?OK,不知道不要紧,看了个人介绍你就明白了。xml
为何要使用视图?
你们都知道,视图自己不存储任何数据,只是一条编译的select语句。数据库会为视图生成一个执行计划,视图是能够重复使用的,由于执行计划也能够重复使用。
视图自己不会带来性能的提高,我曾经觉得它会“记住”查询结果,但后来我才知道它除了是一个编译了的查询外,其它什么都不是,视图根本记不住查询结果,我敢打赌好多刚接触SQL的人都会有这个错误的想法。
可是如今我要告诉你一个方法让视图记住查询结果,其实很是简单,就是在视图上建立索引就能够了。
若是你在视图上应用了索引,视图就成为索引视图,对于一个索引视图,数据库引擎处理SQL,并在数据文件中存储结果,和汇集表相似,当基础表中的数据发生变化时,SQL Server会自动维护索引,所以当你在索引视图上查询时,数据库引擎简单地从索引中查找值,速度固然就很快了,所以在视图上建立索引能够明显加快查询速度。
但请注意,天下没有免费的午饭,建立索引视图能够提高性能,当基础表中的数据发生变化时,数据库引擎也会更新索引,所以,当视图要处理不少行,且要求和,当数据和基础表不常常发生变化时,就应该考虑建立索引视图。
如何建立索引视图?
1)建立/修改视图时指定SCHEMABINDING选项:
2)在视图上建立一个惟一的汇集索引;
3)视须要在视图上建立一个非汇集索引。
不是全部视图上均可以建立索引,在视图上建立索引存在如下限制:
1)建立视图时使用了SCHEMABINDING选项,这种状况下,数据库引擎不容许你改变表的基础结构;
2)视图不能包含任何非肯定性函数,DISTINCT子句和子查询;
3)视图中的底层表必须由汇集索引(主键)。
若是你发现你的应用程序中使用的TSQL是用视图实现的,但存在性能问题,那此时给视图加上索引可能会带来性能的提高。
为用户定义函数(UDF)建立索引
在用户定义函数上也能够建立索引,但不能直接在它上面建立索引,须要建立一个辅助的计算列,公式就使用用户定义函数,而后在这个计算列字段上建立索引。具体步骤以下:
1)首先建立一个肯定性的函数(若是不存在的话),在函数定义中添加SCHEMABINDING选项,如:
2)在目标表上增长一个计算列,使用前面定义的函数做为该列的计算公式,如图2所示。
3)在计算列上建立索引
当你的查询中包括UDF时,若是在该UDF上建立了以计算列为基础的索引,特别是两个表或视图的链接条件中使用了UDF,性能都会有明显的改善。
在XML列上建立索引
在SQL Server(2005和后续版本)中,XML列是以二进制大对象(BLOB)形式存储的,可使用XQuery进行查询,但若是没有索引,每次查询XML数据类型时都很是耗时,特别是大型XML实例,由于SQL Server在运行时须要分隔二进制大对象评估查询。为了提高XML数据类型上的查询性能,XML列能够索引,XML索引分为两类。
主XML索引
建立XML列上的主索引时,SQL Server会切碎XML内容,建立多个数据行,包括元素,属性名,路径,节点类型和值等,建立主索引让SQL Server更轻松地支持XQuery请求。下面是建立一个主XML索引的示例语法。
次要XML索引
虽然XML数据已经被切条,但SQL Server仍然要扫描全部切条的数据才能找到想要的结果,为了进一步提高性能,还须要在主XML索引之上建立次要XML索引。有三种次要XML索引。
1)“路径”(Path)次要XML索引:使用.exist()方法肯定一个特定的路径是否存在时它颇有用;
2)“值”(Value)次要XML索引:用于执行基于值的查询,但不知道完整的路径或路径包括通配符时;
3)“属性”(Secondary)次要XML索引:知道路径时检索属性的值。
下面是一个建立次要XML索引的示例:
请注意,上面讲的原则是基础,若是盲目地在表上建立索引,不必定会提高性能,由于有时在某些表的某些列上建立索引时,可能会导致插入和更新操做变慢,当这个表上有一个低选中性列时更是如此,一样,当表中的记录不多(如<500)时,若是在这样的表上建立索引反倒会使数据检索性能下降,由于对于小表而言,全表扫描反而会更快,所以在建立索引时应放聪明一点。
第七步:应用反范式化,使用历史表和预计算列
反范式化
若是你正在为一个OLTA(在线事务分析)系统设计数据库,主要指为只读查询优化过的数据仓库,你能够(和应该)在你的数据库中应用反范式化和索引,也就是说,某些数据能够跨多个表存储,但报告和数据分析查询在这种数据库上可能会更快。
但若是你正在为一个OLTP(联机事务处理)系统设计数据库,这样的数据库主要执行数据更新操做(包括插入/更新/删除),我建议你至少实施第1、2、三范式,这样数据冗余能够降到最低,数据存储也能够达到最小化,可管理性也会好一点。
不管咱们在OLTP系统上是否应用范式,在数据库上总有大量的读操做(即select查询),当应用了全部优化技术后,若是发现数据检索操做仍然效率低下,此时,你可能须要考虑应用反范式设计了,但问题是如何应用反范式化,以及为何应用反范式化会提高性能?让咱们来看一个简单的例子,答案就在例子中。
假设咱们有两个表OrderDetails(ID,ProductID,OrderQty) 和 Products(ID,ProductName)分别存储订单详细信息和产品信息,如今要查询某个客户订购的产品名称和它们的数量,查询SQL语句以下:
若是这两个都是大表,当你应用了全部优化技巧后,查询速度仍然很慢,这时能够考虑如下反范式化设计:
1)在OrderDetails表上添加一列ProductName,并填充好数据;
2)重写上面的SQL语句
注意在OrderDetails表上应用了反范式化后,再也不须要链接Products表,所以在执行SQL时,SQL引擎不会执行两个表的链接操做,查询速度固然会快一些。
为了提升select操做性能,咱们不得不作出一些牺牲,须要在两个地方(OrderDetails 和 Products表)存储相同的数据(ProductName),当咱们插入或更新Products 表中的ProductName字段时,不得不一样步更新OrderDetails表中的ProductName字段,此外,应用这种反范式化设计时会增长存储资源消耗。
所以在实施反范式化设计时,咱们必须在数据冗余和查询操做性能之间进行权衡,同时在应用反范式化后,咱们不得不重构某些插入和更新操做代码。有一个重要的原则须要遵照,那就是只有当你应用了全部其它优化技术都还不能将性能提高到理想状况时才使用反范式化。同时还需注意不能使用太多的反范式化设计,那样会使本来清晰的表结构设计变得越来模糊。
历史表
若是你的应用程序中有按期运行的数据检索操做(如报表),若是涉及到大表的检索,能够考虑按期将事务型规范化表中的数据复制到反范式化的单一的历史表中,如利用数据库的Job来完成这个任务,并对这个历史表创建合适的索引,那么周期性执行的数据检索操做能够迁移到这个历史表上,对单个历史表的查询性能确定比链接多个事务表的查询速度要快得多。
例如,假设有一个连锁商店的月度报表须要3个小时才能执行完毕,你被派去优化这个报表,目的只有一个:最小化执行时间。那么你除了应用其它优化技巧外,还能够采起如下手段:
1)使用反范式化结构建立一个历史表,并对销售数据创建合适的索引;
2)在SQL Server上建立一个按期执行的操做,每隔24小时运行一次,在半夜往历史表中填充数据;
3)修改报表代码,从历史表获取数据。
建立按期执行的操做
按照下面的步骤在SQL Server中建立一个按期执行的操做,按期从事务表中提取数据填充到历史表中。
1)首先确保SQL Server代理服务处于运行状态;
2)在SQL Server配置管理器中展开SQL Server代理节点,在“做业”节点上建立一个新做业,在“常规”标签页中,输入做业名称和描述文字;
3)在“步骤”标签页中,点击“新建”按钮建立一个新的做业步骤,输入名字和TSQL代码,最后保存;
4)切换到“调度”标签页,点击“新建”按钮建立一个新调度计划;
5)最后保存调度计划。
在数据插入和更新中提早执行耗时的计算,简化查询
大多数状况下,你会看到你的应用程序是一个接一个地执行数据插入或更新操做,一次只涉及到一条记录,但数据检索操做可能同时涉及到多条记录。
若是你的查询中包括一个复杂的计算操做,毫无疑问这将致使总体的查询性能降低,你能够考虑下面的解决办法:
1)在表中建立额外的一列,包含计算的值;
2)为插入和更新事件建立一个触发器,使用相同的计算逻辑计算值,计算完成后更新到新建的列;
3)使用新建立的列替换查询中的计算逻辑。
实施完上述步骤后,插入和更新操做可能会更慢一点,由于每次插入和更新时触发器都会执行一下,但数据检索操做会比以前快得多,由于执行查询时,数据库引擎不会执行计算操做了。
小结
至此,咱们已经应用了索引,重构TSQL,应用高级索引,反范式化,以及历史表加速数据检索速度,但性能优化是一个永无终点的过程,最下一篇文章中咱们将会介绍如何诊断数据库性能问题。
诊断数据库性能问题就象医生诊断病人病情同样,既要结合本身积累的经验,又要依靠科学的诊断报告,才能准确地判断问题的根源在哪里。前面三篇文章咱们介绍了许多优化数据库性能的方法,当然掌握优化技巧很重要,但诊断数据库性能问题是优化的前提,本文就介绍一下如何诊断数据库性能问题。