SQL server 存儲過程編寫經驗和優化措施

 存儲過程編寫經驗和優化措施 程序員

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
相關文章
相關標籤/搜索