假设你第一次主导一个基于SQL Server的项目,而以前是基于Access。那么你可能有一些SQL Server的性能上的问题,不知道怎么来处理。如果你想简要地知道一些关于SQL Server及其数据访问层解决方案的指导方针,那么这篇文章就是你所需要的。
即使你不使用SQL Server,这里的大部分指导方针也都可以运用到其它的数据库管理系统,如Sybase、Oracle。当然,这里没有T-SQL的诀窍,也没有针对你的SQL Server疑问的灵丹妙药。我想说的是健全的设计相关的一些建议,这些建议来自于我之前几年的学习和一次次设计失误的积累。
了解你的工具
请不要轻视这个工具。这是本文的重点。你将诧异有许多编程人员不了解所有T-SQL命令和所有SQL Server有用工具。“什么?我需要花费一个月的时间来学习我从来不使用的SQL命令??”你不应该这样说,应当花上一个周末来浏览MSDN上的所有T-SQL命令,从而学到许多应该做和不应该做的。这样,不久以后,当你在设计一个查询语句的时候,你就会知道:“这个命令我还不熟悉”,然后回到MSDN去看它正确的语法。
在本文中,假设你已经了解T-SQL的语法或者能从MSDN找到。
由于作者针对的目标是初学者,所以这么说)
让我强调一下:不要使用游标。他们是一个完整系统的性能杀手。许多初学者使用游标,却不知道牺牲了多少性能!游标使用内存,以某种方式锁定表,并且效率低下。更糟糕的是,游标和你的数据库管理员的许多性能优化相抵触。你知道每个FETCH的执行性能与一条SELECT语句一样吗?换句话说,如果你对10000条记录的表使用游标,相当与对该表做10000次的SELECT查询!如果你使用几个SELECT、UPDATE或DELECT来取代游标,操作将会快得多。
SQL程序员初学者因为游标的好用和类似编程的代码而使用它。嗯,不幸的是,这会导致糟糕的性能。SQL的最终目的是说明你想做什么,而不是你应该怎么去做它。
我曾经重写过一个基于游标的存储过程,用一些基本的SQL查询语句来取代它。相关的表有100000条记录,基于游标的存储过程执行一次需要40分钟,而新的存储过程仅仅需要10秒,你应该看看写基于游标的存储过程的可怜的程序员的表情!
有时候,创建可以一个小的应用程序来获得全部数据,处理后更新到服务器,这样反而会更快!T-SQL没有想像中那么好的循环性能。
如果你正在看这篇文章,我想说一下:使用游标是不好的,我从来没有看到游标能合适地使用到哪,除了用于数据库管理员的某些工作。因为大部分时候,管理员知道他们在做什么,但是读者你应该不是一个专门的数据库管理员,不是吗?
规范化你的数据表
有两个可以不做规范化数据库的一般理由:性能和纯属偷懒。不久以后你将为你的懒惰付出代价,对于性能,如果不是速度很慢,就不应该去刻意优化它。我经常看到许多程序员因为速度慢而使数据库不规范。而更经常的,修改后的结果反而是更加地慢。数据库管理系统设计用于规范化的数据库,所以请规范化地设计你的数据库。
不要使用SELECT *
改变老习惯是很难的,我知道,坦白地说我也经常使用SELECT *,但是请尽可能地仅写出你所需要的列,这样有许多好处:
1.减少内存消耗,节省带宽
2.轻松的安全设计
3.让查询优化器能通过索引读取出所有需要的列
了解你的数据怎样存取的
你能为你的数据库做的正确事情之一是一个健壮的索引设计。这项工作简直是一项艺术。每当你为数据表增加一个索引,SELECT操作将更加快,但是INSERT和DELETE操作将变得更慢。建立和维护一个索引有许多工作要做。如果你为了加速表的SELECT而增加一些索引时,你将发现在UPDATE时表将长时间锁定以更新索引。因此,问题在于:这个表用于干吗?读取还是更新数据?这个问题要谨慎考虑,特别对于DELETE和UPDATE操作,因为两个操作经常包含一个SELECT作为WHERE部分的更新或删除条件。
不要为如“性别”之类的列创建索引
这是徒劳的。首先,让我们了解索引是怎样来加速表存取的。你知道索引是通过将表按一个标准快速地进行划分的方法来实现的。如果你为类似于“性别”列创建一个索引,整个表将划分为两个部分:男性和女性。如果你的表有1000000条记录,它能进行什么优化呢?记住,维持一个索引是很慢的。应该按照从最稀松的列(即极少值相同)到最不稀松的列的顺序来设计你的索引,如姓名+省份+性别。
使用事务
特别为运行时间较长的查询。它将在错误发生的时候拯救你。进行数据编程一段时间后,你会发现总有一些意外的状况使你的存储过程崩溃。
注意死锁
请总是按照相同的顺序存取你的数据表。当你使用存储过程和事务的时候,你将明白这一点。如果你要锁定表A后,锁定表B,请在你的所有存储过程中总是按照这个顺序锁定。如果你在另外的存储过程中,先锁定表B,然后锁定表B,将因为你锁定顺序的粗心设计导致麻烦的死锁。
不要开启一个大的数据集
编程论坛中一个常见的问题是:“我怎样才能迅速地填充100000个项到下拉列表框中?”。嗯,这个非常不正确的。你不能怎么做,也不应该这么做。最起码,你的用户很讨厌从100000个项中选择一项,你需要一个更加友好的用户界面,你应该显示给你的用户不超过100或200条的数据。
不要使用服务器端游标
除非你知道你在干吗。客户端游标经常(不总是)花费更少的网络资源和服务器资源,缩短锁定时间。
使用带参数的查询
有时我在编程论坛上,看到类似如下的问题:“我的查询因为一些字符(如单引号)失败了,我怎么才能避免呢?”通常的回答是:“改单引号为双引号。”错了,这仅仅是一个问题的解决,其它字符还有可能引起查询失败,而且有可能引入严重的安全错误。另外,它还会破坏SQL Server缓存系统。应该学习怎样使用带参数的查询,彻底避免发生类似的错误。
总是使用一个大的数据库做测试
程序员开发时,通常使用一个小的测试数据库,而最终用户使用的是一个大数据库。这是错误的:磁盘很便宜,而使用小数据库性能问题将很晚才会发现。
不要使用INSERT语句导入大量的数据
除非确实需要,使用DTS或者BCP工具,你将有一个比较灵活和快速的解决方案。
注意超时
查询数据库时,默认的超时是很短的,如15秒或30秒。记得告诉数据库,查询可以长时间运行而不是15秒或30秒,尤其当你的数据库增长时。
不要忽视同时发生的编辑
有时两个用户可能同时编辑同一条记录。当写入时,后面的写用户将成功,而前面用户的更改将丢失。很容易发现这种情况:创建一个时间戳列,并且当你写入的时候检查它。如果可能,合并这些改变。如果两个更新有冲突,及时地提示用户。
在往明细表中插入数据时,不要执行SELECT Max(ID) FROM 主表
这是又一个经常发生的错误。当有两个用户同时插入数据时,SELECT查询将无效。尽可能使用SCOPE_IDENTITY,IDENT_CURRENT和@@IDENTITY之一来避免导致的触发器脏读问题。
尽量避免可空列
尽可能地。可空列在每一行多消耗一个特殊的字节,当查询数据时将产生更多的资源消耗。数据访问层的编程会很棘手,因为每次你存取可空列时都必须检查它。
我并不是说可空列像有些人说的是恶魔的化身。我相信它们能很好的使用,并且当“可空的数据”是你的业务规则的一部分时,它们能简化你的代码。但是,有时候可空列被使用在下列情形:
CustomerName1
CustomerAddress1
CustomerEmail1
CustomerName2
CustomerAddress2
CustomerEmail3
CustomerName1
CustomerAddress2
CustomerEmail3
那么,这是极其糟糕的。请不要这样做,将你的数据表规范化。这样数据表将更加灵活和快捷,减少可空列。
不要使用Text数据类型
除非你真的因为大型数据使用它。对查询来说,如果不正确地使用,Text数据类型是不灵活的,速度慢的,而且将浪费大量的空间。有时一个VARCHAR类型能更好地处理你的数据。
不要使用临时表
除非真正地需要。通常地,一个子查询可以用一个临时表来取代。它们可以减少花销,但是当在COM+下编程时使用数据库连接池,临时表将总是存在,这将令人头疼。在SQL Server2000中,另外一个选择是提供一个写在存储过程里的小型表,它将驻留在内存中。
学习怎样看懂一个查询执行计划
SQL Server查询分析器是你的朋友,你能从中学习到许多东西,如语句如何被执行,查询和索引能怎样影响性能。
使用参照完整性
它能非常的节约时间。定义所有该有的主键,唯一约束和外键。每个在服务器上建立的验证以后都会节省你的时间。
结论
正如我前面说的,本文不是一个完全的SQL Server性能和最佳实践的向导。那些内容足够写一本书了。但是,我相信本文是一个很好的开端,如果你遵循这些习惯,在以后的实践中将减少很多的麻烦。