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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章