SQL server 存储过程编写经验和优化措施

 存储过程编写经验和优化措施 

From:网页教学网
  一、适合读者对象:数据库开发程序员,数据库的数据量很多,涉及到对SP(存储过程)的优化的项目开发人员,对数据库有浓厚兴趣的人。  
  二、介绍:在数据库的开发过程中,经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。如果项目的SP较多,书写又没有一定的规范,将会影响以后的系统维护困难和大SP逻辑的难以理解,另外如果数据库的数据量大或者项目对SP 的性能要求很,就会遇到优化的问题,否则速度有可能很慢,经过亲身经验,一个经过优化过的SP要比一个性能差的SP的效率甚至高几百倍。  
  三、内容:  
  1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。  
  2、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,做过自身的查询优化检查。  
  3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:   
  a)SQL的使用规范:
   i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。
   ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。
   iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。
   iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。
   v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
   vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。
   vii. 尽量使用“>=”,不要使用“>”。
   viii. 注意一些or子句和union子句之间的替换
   ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。
   x. 注意存储过程中参数和数据类型的关系。
   xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。   
  b)索引的使用规范:
   i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。
   ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引
   iii. 避免对大表查询时进行table scan,必要时考虑新建索引。
   iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
   v. 要注意索引的维护,周期性重建索引,重新编译存储过程。  
  c)tempdb的使用规范:
   i. 尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。
   ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。
   iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。
   iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。
    v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
    vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。  
  d)合理的算法使用:   
  根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。
解析:Microsoft SQL Server中的锁模式
在SQL Server数据库中加锁时,除了可以对不同的资源加锁,还可以使用不同程度的加锁方式,即锁有多种模式,SQL Server中锁模式包括:
1.共享锁 SQL Server中,共享锁用于所有的只读数据操作。共享锁是非独占的,允许多个并发事务读取其锁定的资源。默认情况下,数据被读取后,SQL Server立即释放共享锁。例如,执行查询“SELECT * FROM AUTHORS”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。但是,事务隔离级别连接选项设置和SELECT语句中的锁定设置都可以改变SQL Server的这种默认设置。例如,“ SELECT * FROM AUTHORS HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。
2.更新锁更新锁在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个事务申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这时,这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。
3.排它锁 排它锁是为修改数据而保留的。它所锁定的资源,其他事务不能读取也不能修改。
4.结构锁 执行表的数据定义语言 (DDL) 操作(例如添加列或除去表)时使用架构修改 (Sch-M) 锁。当编译查询时,使用架构稳定性 (Sch-S) 锁。架构稳定性 (Sch-S) 锁不阻塞任何事务锁,包括排它锁。因此在编译查询时,其它事务(包括在表上有排它锁的事务)都能继续运行。但不能在表上执行 DDL 操作。
5.意向锁 意向锁说明SQL Server有在资源的低层获得共享锁或排它锁的意向。例如,表级的共享意向锁说明事务意图将排它锁释放到表中的页或者行。意向锁又可以分为共享意向锁、独占意向锁和共享式独占意向锁。共享意向锁说明事务意图在共享意向锁所锁定的低层资源上放置共享锁来读取数据。独占意向锁说明事务意图在共享意向锁所锁定的低层资源上放置排它锁来修改数据。共享式排它锁说明事务允许其他事务使用共享锁来读取顶层资源,并意图在该资源低层上放置排它锁。
6.大容量更新锁 当将数据大容量复制到表,且指定了 TABLOCK 提示或者使用 sp_tableoption 设置了 table lock on bulk 表选项时,将使用大容量更新锁。大容量更新锁允许进程将数据并发地大容量复制到同一表,同时防止其它不进行大容量复制数据的进程访问该表。
详细介绍优化SQL Server 2000的设置
  SQL Server已经为了优化自己的性能而进行了良好的配置,比今天市场其他的关系型数据库都要好得多。然而,你仍然有几项设置需要进行修改,以便你的数据库每分钟可以处理更多的事务(TPM)。本篇文章的目的就是讨论这些设置。我们忽略那些可以通过硬件配置或者表或者索引设计提高的性能,因为这些内容在本篇文章范围之外。
  破碎页面检测
  在我们开始讨论服务器配置开关之前,让我们快速浏览一下你的模型数据库--或者说用作构建新的数据库的基础的模板。默认情况下,你可以在数据库中创建存储过程、函数等类似的东西,随后他们将会被加入新创建的数据库中。
  要优化性能,你也许想要关闭模型数据库中的破碎页面检测。当一个页面被成功写入磁盘的时候,破碎页面检测进行识别。如果激活了的话,你可以看到每个写操作对性能产生的每个细小的影响。大多数现代的磁盘阵列都有板上电池,使得阵列可以在突然断电的情况下完成所有的写操作--引起破碎页面的最频繁原因。
  以下的步骤可以接受如何关闭破碎页面检测:
  
exec sp_dboption 'model', 'torn page detection', 'false'
 
  这篇基础知识资源可以为你提供更多有关这个设置的信息。
  大多数的配置是通过系统存储过程sp_configure完成的。要显示服务器的全部设置列表以便定制,你可以输入如下命令:
 
 sp_configure 'show advanced options', 1
 
  GO
 
  RECONFIGURE WITH OVERRIDE
 
 
  你可以配置的选项的数量根据你的SQL Server的版本、服务包,以及位数版本(64位的SQL Server比32位的选项要多)而定。我将直接讨论最能影响SQL Server性能优化的选项。
  Affinity mask: Affinity mask让你可以控制SQL Server使用哪个处理器。对于大多数情况,你不应该接触这个设置,让操作系统控制处理器关系。然而,你也许想要用这个选项来将某个处理器专门用于另一个进程(例如,MSSearch 或者 SQL Server磁盘 IO ,以及 SQL Server的平衡)。参考基础知识资源获取更多有关这个设置的信息。
  Awe enabled: Awe的启动可以让SQL Server Enterprise版本运行在Windows 2000以及以上高级服务器上,或者Windows 2003 Enterprise以及以上的版本使用超过4GB的内存。如果你的服务器符合这些条件的话,就激活这个设置吧。
  并行成本极限:当查询需要进行并行处理的时候,并行的成本极限就定下来了。默认情况是五秒钟。将这个数值改为稍低的数值,俄可以让更多个查询获得并行处理,但是这也会引起CPU瓶颈。这个设置只有在多个处理器的机器上才会起作用。
  填充因子:填充因子设置了在创建聚簇索引的时候用来自动填充的因子。在频繁插入的表中,将数值从默认的90%设置为较低的数值,你会获得收益。
  轻量级缓冲池:这个设置启动了光纤模式。使用这个选项在CPU利用率很高的8路及其以上的服务器上。这可以让光纤同时为每个线程提供服务,同时在默认情况下运行在每个处理器上。某些任务可以从这些光纤中获得优势。
  并行的最大程度:当服务器可以使用并行或者不能使用并行,或者是当某个数量的处理器可以用于并行操作的时候,这个设置就确定了。并行就是多个处理器上发生多个处理。例如,查询的并行操作可以在不同的处理器上同时处理。
  服务器最大内存(MB):如果你在SQL Server上运行了其他的处理,并且有足够的内存,那么你有可能想要留出512MB的内存给操作系统和这些进程。例如,你可以在MSSearch或者在本地运行大量的代理的情况下将其设置为512。
最大工作线程:最大工作线程设置与ADO.net中的连接池有些类似。通过这个设置,任何超过限制(255个用户)的用户连接都可以在线程池中等待,直到为某个连接服务的线程得到释放,就好像是ADO.net中的连接与连接池共享。如果你有很大量的连接,并且大量的内存,那么你就可以提高这个数值。
  网络包尺寸(B):这个设置控制了网络中传输到你的客户端的包的尺寸。在有损耗的网络中(例如电话线),你可能想要将这个参数设置为比较低的数值,墨人数值是4096。在连接良好的网络中,你可以提高这个设置,特别是涉及BLOB的大型批处理操作。
  优先推进:这个设置为SQL Server提供了处理器的推动。在任务管理器中,点击进程标签,定位SQL Server的位置,然后右击它。选择“设置优先级别”。注意,SQL Server应该运行在正常的优先级别上。输入如下命令:
 
  Sp_configure 'priority boost', 1
 
  Reconfigure with override
 
 
  然后重新启动你的SQL Server。在任务管理器中察看SQL Server现在运行在什么优先级别上。它应该是在高优先级上。SQL Server应该比其他的用户进程运行优先级别要高。在专用于SQL Server的服务器上使用这个设置。
总结
  本篇讨论了最常见的SQL Server优化设置。在做出改变之前和之后分别在测试环境中进行基线确定是非常重要的,可以据此来评估在典型的负载下,改变对你的系统的影响。
SQL Server 数据库中关于死锁的分析
SQL Server数据库发生死锁时不会像ORACLE那样自动生成一个跟踪文件。有时可以在[管理]->[当前活动] 里看到阻塞信息(有时SQL Server企业管理器会因为锁太多而没有响应).
设定跟踪1204:
 
USE MASTER
 
DBCC TRACEON (1204,-1)
 
 
显示当前启用的所有跟踪标记的状态:
DBCC TRACESTATUS(-1)
取消跟踪1204:
DBCC TRACEOFF (1204,-1)
在设定跟踪1204后,会在数据库的日志文件里显示SQL Server数据库死锁时一些信息。但那些信息很难看懂,需要对照SQL Server联机丛书仔细来看。根据PAG锁要找到相关数据库表的方法:
DBCC TRACEON (3604)
DBCC PAGE (db_id,file_id,page_no)
DBCC TRACEOFF (3604)
请参考sqlservercentral.com上更详细的讲解.但又从CSDN学到了一个找到死锁原因的方法。我稍加修改, 去掉了游标操作并增加了一些提示信息,写了一个系统存储过程sp_who_lock.sql。代码如下:
 
if exists (select * from dbo.sysobjects
where id = object_id(N'[dbo].[sp_who_lock]')
and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[sp_who_lock]
GO
/********************************************************
//  创建 : fengyu  邮件 : [email protected]
//  日期 :2004-04-30
//  修改 : 从http://www.csdn.net/develop/Read_Article.asp?id=26566
//  学习到并改写
//  说明 : 查看数据库里阻塞和死锁情况
********************************************************/
use master
go
create procedure sp_who_lock
as
begin
declare @spid int,@bl int,
@intTransactionCountOnEntry     int,
@intRowcount             int,
@intCountProperties         int,
@intCounter             int
create table #tmp_lock_who (
id int identity(1,1),
spid smallint,
bl smallint)
IF @@ERROR<>0 RETURN @@ERROR
insert into #tmp_lock_who(spid,bl) select  0 ,blocked
from (select * from sysprocesses where  blocked>0 ) a
where not exists(select * from (select * from sysprocesses
where  blocked>0 ) b
where a.blocked=spid)
union select spid,blocked from sysprocesses where  blocked>0
IF @@ERROR<>0 RETURN @@ERROR
-- 找到临时表的记录数
select     @intCountProperties = Count(*),@intCounter = 1
from #tmp_lock_who
IF @@ERROR<>0 RETURN @@ERROR
if    @intCountProperties=0
select '现在没有阻塞和死锁信息' as message
-- 循环开始
while @intCounter <= @intCountProperties
begin
-- 取第一条记录
select     @spid = spid,@bl = bl
from #tmp_lock_who where Id = @intCounter
begin
if @spid =0
select '引起数据库死锁的是: '+ CAST(@bl AS VARCHAR(10))
+ '进程号,其执行的SQL语法如下'
else
select '进程号SPID:'+ CAST(@spid AS VARCHAR(10))+ '被'
+ '进程号SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其当前进程执行的SQL语法如下'
DBCC INPUTBUFFER (@bl )
end
-- 循环指针下移
set @intCounter = @intCounter + 1
end
drop table #tmp_lock_who
return 0
end
 
 
需要的时候直接调用:
sp_who_lock
就可以查出引起死锁的进程和SQL语句.
SQL Server自带的系统存储过程sp_who和sp_lock也可以用来查找阻塞和死锁, 但没有这里介绍的方法好用。如果想知道其它tracenum参数的含义,请看www.sqlservercentral.com文章
我们还可以设置锁的超时时间(单位是毫秒), 来缩短死锁可能影响的时间范围:
例如:
 
use master
seelct @@lock_timeout
set lock_timeout 900000
-- 15分钟
seelct @@lock_timeout

你可能感兴趣的:(优化,开发,server,数据库操作,教学网)