存储过程编写经验和优化措施 程序员
From:网页教学网
1、适合读者对象:数据库开发程序员,数据库的数据量不少,涉及到对SP(存储过程)的优化的项目开发人员,对数据库有浓厚兴趣的人。
2、介绍:在数据库的开发过程当中,常常会遇到复杂的业务逻辑和对数据库的操做,这个时候就会用SP来封装数据库操做。若是项目的SP较多,书写又没有必定的规范,将会影响之后的系统维护困难和大SP逻辑的难以理解,另外若是数据库的数据量大或者项目对SP 的性能要求很,就会遇到优化的问题,不然速度有可能很慢,通过亲身经验,一个通过优化过的SP要比一个性能差的SP的效率甚至高几百倍。
3、内容:
一、开发人员若是用到其余库的Table或View,务必在当前库中创建View来实现跨库操做,最好不要直接使用“databse.dbo.table_name”,由于sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。
二、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,作过自身的查询优化检查。
三、高程序运行效率,优化应用程序,在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 邮件 : maggiefengyu@tom.com
// 日期 :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