轉載:SqlServer數據庫性能優化詳解

本文轉載自:http://blog.csdn.net/andylaudotnet/article/details/1763573程序員

       性能調節的目的是經過將網絡流通、磁盤 I/O 和 CPU 時間減到最小,使每一個查詢的響應時間最短並最大限度地提升整個數據庫服務器的吞吐量。爲達到此目的,須要瞭解應用程序的需求和數據的邏輯和物理結構,並在相互衝突的數據庫使用之間(如聯機事務處理 (OLTP) 與決策支持)權衡。web

對性能問題的考慮應貫穿於開發階段的全過程,不該只在最後實現系統時才考慮性能問題。許多使性能獲得顯著提升的性能事宜可經過開始時仔細設計得以實現。爲最有效地優化 Microsoft? SQL Server? 2000 的性能,必須在極爲多樣化的情形中識別出會使性能提高最多的區域,並對這些區域集中分析。算法

雖然其它系統級性能問題(如內存、硬件等)也是研究對象,但經驗代表從這些方面得到的性能收益一般會增加。一般狀況下,SQL Server 自動管理可用的硬件資源,從而減小對大量的系統級手動調節任務的需求(以及從中所得的收益)。sql

設計聯合數據庫服務器數據庫

爲達到大型 Web 站點所需的高性能級別,多層系統通常在多個服務器之間平衡每一層的處理負荷。Microsoft? SQL Server? 2000經過對 SQL Server 數據進行水平分區,在一組服務器之間分攤數據庫處理負荷。這些服務器相互獨立,但也能夠相互協做以處理來自應用程序的數據庫請求;這樣的一組協做服務器稱爲聯合體。編程

只有當應用程序將每一個 SQL 語句發送到擁有該語句所需的大部分數據的成員服務器時,聯合數據庫層才能夠達到很是高的性能級別。這稱爲使用語句所需的數據配置 SQL 語句。使用所需的數據配置 SQL 語句不是聯合服務器所獨有的要求;在羣集系統中一樣有此要求。緩存

雖然服務器聯合體與單個數據庫服務器呈現給應用程序的圖像相同,但在實現數據庫服務層的方式上存在內部差別。性能優化

單個服務器層服務器

聯合服務器層網絡

生產服務器上有一個 SQL Server 實例。

每一個成員服務器上都有一個 SQL Server 實例。

生產數據存儲在一個數據庫中。

每一個成員服務器都有一個成員數據庫。數據分佈在成員數據庫之間。

通常每一個表都是單個實體。

原始數據庫中的表被水平分區爲成員表。一個成員數據庫有一個成員表,並且使用分佈式分區視圖使每一個成員服務器上看起來彷佛都有原始表的完整複本。

與單個服務器的全部鏈接和全部 SQL 語句都由 SQL Server 的同一個實例處理。

應用程序層必須可以在包含語句所引用的大部分數據的成員服務器上配置 SQL 語句。

原始數據庫中的表被水平分區爲成員表。一個成員數據庫有一個成員表,並且使用分佈式分區視圖使每一個成員服務器上看起來彷佛都有原始表的完整複本。

與單個服務器的全部鏈接和全部 SQL 語句都由 SQL Server 的同一個實例處理。

應用程序層必須可以在包含語句所引用的大部分數據的成員服務器上配置 SQL 語句。

雖然目的是設計數據庫服務器聯合體來處理所有的工做負荷,可是可經過設計一組在不一樣的服務器之間分佈數據的分佈式分區視圖來達到此目的。

數據庫設計

數據庫的設計包括兩個組成部分:邏輯設計和物理設計。邏輯數據庫設計包括使用數據庫組件(如表和約束)爲業務需求和數據建模,而無須考慮如何或在哪裏物理存儲這些數據。物理數據庫設計包括將邏輯設計映射到物理媒體上、利用可用的硬件和軟件功能使得儘量快地對數據進行物理訪問和維護,還包括生成索引。要在設計後更改這些組件很困難,所以在數據庫應用程序開發的早期階段正確設計數據庫、使其爲業務需求建模並利用硬件和軟件功能很重要。

實現SQL Server數據庫的優化,首先要有一個好的數據庫設計方案。在實際工做中,許多SQL Server方案每每是因爲數據庫設計得很差致使性能不好。實現良好的數據庫設計必須考慮這些問題:

1.1 邏輯庫規範化問題

通常來講,邏輯數據庫設計會知足規範化的前3級標準:

1.第1規範:沒有重複的組或多值的列。

2.第2規範:每一個非關鍵字段必須依賴於主關鍵字,不能依賴於1個組合式主關鍵字的某些組成部分。

3.第3規範:1個非關鍵字段不能依賴於另1個非關鍵字段。

  遵照這些規則的設計會產生較少的列和更多的表,於是也就減小了數據冗餘,也減小了用於存儲數據的頁。但表關係也許須要經過複雜的合併來處理,這樣會下降系統的性能。某種程度上的非規範化能夠改善系統的性能,非規範化過程能夠根據性能方面不一樣的考慮用多種不一樣的方法進行,但如下方法經實踐驗證每每能提升性能。

1.若是規範化設計產生了許多4路或更多路合併關係,就能夠考慮在數據庫實體(表)中加入重複屬性(列)

2.經常使用的計算字段(如總計、最大值等)能夠考慮存儲到數據庫實體中。

  好比某一個項目的計劃管理系統中有計劃表,其字段爲:項目編號、年初計劃、二次計劃、調整計劃、補列計劃…,而計劃總數(年初計劃+二次計劃+調整計劃+補列計劃)是用戶常常須要在查詢和報表中用到的,在表的記錄量很大時,有必要把計劃總數做爲1個獨立的字段加入到表中。這裏能夠採用觸發器以在客戶端保持數據的一致性。

3.從新定義實體以減小外部屬性數據或行數據的開支。相應的非規範化類型是:

  (1)把1個實體(表)分割成2個表(把全部的屬性分紅2組)。這樣就把頻繁被訪問的數據同較少被訪問的數據分開了。這種方法要求在每一個表中複製首要關鍵字。這樣產生的設計有利於並行處理,並將產生列數較少的表。

  (2)把1個實體(表)分割成2個表(把全部的行分紅2組)。這種方法適用於那些將包含大量數據的實體(表)。在應用中常要保留歷史記錄,可是歷史記錄不多用到。所以能夠把頻繁被訪問的數據同較少被訪問的歷史數據分開。並且若是數據行是做爲子集被邏輯工做組(部門、銷售分區、地理區域等)訪問的,那麼這種方法也是頗有好處的。

 1.2 生成物理數據庫

  要想正確選擇基本物理實現策略,必須懂得數據庫訪問格式和硬件資源的操做特色,主要是內存和磁盤子系統I/O。這是一個範圍普遍的話題,但如下的準則可能會有所幫助。

  1.與每一個表列相關的數據類型應該反映數據所需的最小存儲空間,特別是對於被索引的列更是如此。好比能使用smallint類型就不要用integer類型,這樣索引字段能夠被更快地讀取,並且能夠在1個數據頁上放置更多的數據行,於是也就減小了I/O操做。

  2.把1個表放在某個物理設備上,再經過SQL Server段把它的不分簇索引放在1個不一樣的物理設備上,這樣能提升性能。尤爲是系統採用了多個智能型磁盤控制器和數據分離技術的狀況下,這樣作的好處更加明顯。

  3.用SQL Server段把一個頻繁使用的大表分割開,並放在2個單獨的智能型磁盤控制器的數據庫設備上,這樣也能夠提升性能。由於有多個磁頭在查找,因此數據分離也能提升性能。

  4.用SQL Server段把文本或圖像列的數據存放在1個單獨的物理設備上能夠提升性能。1個專用的智能型的控制器能進一步提升性能。

查詢優化

查詢速度慢的緣由不少,常見以下幾種:  

  一、沒有索引或者沒有用到索引(這是查詢慢最多見的問題,是程序設計的缺陷)  

  二、I/O吞吐量小,造成了瓶頸效應。  

  三、沒有建立計算列致使查詢不優化。  

  四、內存不足  

  五、網絡速度慢  

  六、查詢出的數據量過大(能夠採用屢次查詢,其餘的方法下降數據量)  

  七、鎖或者死鎖(這也是查詢慢最多見的問題,是程序設計的缺陷)  

  八、sp_lock,sp_who,活動的用戶查看,緣由是讀寫競爭資源。  

  九、返回了沒必要要的行和列  

     十、查詢語句很差,沒有優化

       能夠經過以下方法來優化查詢 :  

  一、把數據、日誌、索引放到不一樣的I/O設備上,增長讀取速度,之前能夠將Tempdb應放在RAID0上,SQL2000不在支持。數據量(尺寸)越大,提升I/O越重要.  

  二、縱向、橫向分割表,減小表的尺寸(sp_spaceuse)  

  三、升級硬件  

  四、根據查詢條件,創建索引,優化索引、優化訪問方式,限制結果集的數據量。注意填充因子要適當(最好是使用默認值0)。索引應該儘可能小,使用字節數小的列建索引好(參照索引的建立),不要對有限的幾個值的字段建單一索引如性別字段  

  五、提升網速;  

  六、擴大服務器的內存,Windows 2000和SQL server 2000能支持4-8G的內存。配置虛擬內存:虛擬內存大小應基於計算機上併發運行的服務進行配置。運行 Microsoft SQL Server? 2000 時,可考慮將虛擬內存大小設置爲計算機中安裝的物理內存的 1.5 倍。若是另外安裝了全文檢索功能,並打算運行 Microsoft 搜索服務以便執行全文索引和查詢,可考慮:將虛擬內存大小配置爲至少是計算機中安裝的物理內存的 3 倍。將 SQL Server max server memory 服務器配置選項配置爲物理內存的 1.5 倍(虛擬內存大小設置的一半)。  

  七、增長服務器 CPU個數;可是必須明白並行處理串行處理更須要資源例如內存。使用並行仍是串行程是MsSQL自動評估選擇的。單個任務分解成多個任務,就能夠在處理器上運行。例如耽擱查詢的排序、鏈接、掃描和GROUP BY字句同時執行,SQL SERVER根據系統的負載狀況決定最優的並行等級,複雜的須要消耗大量的CPU的查詢最適合並行處理。可是更新操做Update,Insert, Delete還不能並行處理。  

  八、若是是使用like進行查詢的話,簡單的使用index是不行的,可是全文索引,耗空間。 like 'a%' 使用索引 like '%a' 不使用索引用 like '%a%' 查詢時,查詢耗時和字段值總長度成正比,因此不能用CHAR類型,而是VARCHAR。對於字段的值很長的建全文索引。  

  九、DB Server 和APPLication Server 分離;OLTP和OLAP分離  

  十、分佈式分區視圖可用於實現數據庫服務器聯合體。聯合體是一組分開管理的服務器,但它們相互協做分擔系統的處理負荷。這種經過分區數據造成數據庫服務器聯合體的機制可以擴大一組服務器,以支持大型的多層 Web 站點的處理須要。有關更多信息,參見設計聯合數據庫服務器。(參照SQL幫助文件'分區視圖')  

  a、在實現分區視圖以前,必須先水平分區表  

  b、在建立成員表後,在每一個成員服務器上定義一個分佈式分區視圖,而且每一個視圖具備相同的名稱。這樣,引用分佈式分區視圖名的查詢能夠在任何一個成員服務器上運行。系統操做如同每一個成員服務器上都有一個原始表的複本同樣,但其實每一個服務器上只有一個成員表和一個分佈式分區視圖。數據的位置對應用程序是透明的。  

  十一、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收縮數據和日誌 DBCC SHRINKDB,DBCC SHRINKFILE. 設置自動收縮日誌.對於大的數據庫不要設置數據庫自動增加,它會下降服務器的性能。在T-sql的寫法上有很大的講究,下面列出常見的要點:首先,DBMS處理查詢計劃的過程是這樣的:  

   一、查詢語句的詞法、語法檢查  

   二、將語句提交給DBMS的查詢優化器  

   三、優化器作代數優化和存取路徑的優化  

   四、由預編譯模塊生成查詢規劃  

   五、而後在合適的時間提交給系統處理執行  

   六、最後將執行結果返回給用戶其次,看一下SQL SERVER的數據存放的結構:一個頁面的大小爲8K(8060)字節,8個頁面爲一個盤區,按照B樹存放。  

  十二、Commit和rollback的區別 Rollback:回滾全部的事物。 Commit:提交當前的事物. 沒有必要在動態SQL裏寫事物,若是要寫請寫在外面如: begin tran exec(@s) commit trans 或者將動態SQL 寫成函數或者存儲過程。  

  1三、在查詢Select語句中用Where字句限制返回的行數,避免表掃描,若是返回沒必要要的數據,浪費了服務器的I/O資源,加劇了網絡的負擔下降性能。若是表很大,在表掃描的期間將表鎖住,禁止其餘的聯接訪問表,後果嚴重。  

  1四、SQL的註釋申明對執行沒有任何影響

  1五、儘量不使用光標,它佔用大量的資源。若是須要row-by-row地執行,儘可能採用非光標技術,如:在客戶端循環,用臨時表,Table變量,用子查詢,用Case語句等等。遊標能夠按照它所支持的提取選項進行分類:只進必須按照從第一行到最後一行的順序提取行。FETCH NEXT 是惟一容許的提取操做,也是默認方式。可滾動性能夠在遊標中任何地方隨機提取任意行。遊標的技術在SQL2000下變得功能很強大,他的目的是支持循環。有四個併發選項 READ_ONLY:不容許經過遊標定位更新(Update),且在組成結果集的行中沒有鎖。 OPTIMISTIC WITH valueS:樂觀併發控制是事務控制理論的一個標準部分。樂觀併發控制用於這樣的情形,即在打開遊標及更新行的間隔中,只有很小的機會讓第二個用戶更新某一行。當某個遊標以此選項打開時,沒有鎖控制其中的行,這將有助於最大化其處理能力。若是用戶試圖修改某一行,則此行的當前值會與最後一次提取此行時獲取的值進行比較。若是任何值發生改變,則服務器就會知道其餘人已更新了此行,並會返回一個錯誤。若是值是同樣的,服務器就執行修改。選擇這個併發選項OPTIMISTIC WITH ROW VERSIONING:此樂觀併發控制選項基於行版本控制。使用行版本控制,其中的表必須具備某種版本標識符,服務器可用它來肯定該行在讀入遊標後是否有所更改。在 SQL Server 中,這個性能由 timestamp 數據類型提供,它是一個二進制數字,表示數據庫中更改的相對順序。每一個數據庫都有一個全局當前時間戳值:@@DBTS。每次以任何方式更改帶有timestamp 列的行時,SQL Server 先在時間戳列中存儲當前的 @@DBTS 值,而後增長 @@DBTS 的值。若是某個表具備timestamp 列,則時間戳會被記到行級。服務器就能夠比較某行的當前時間戳值和上次提取時所存儲的時間戳值,從而肯定該行是否已更新。服務器沒必要比較全部列的值,只需比較 timestamp 列便可。若是應用程序對沒有 timestamp 列的表要求基於行版本控制的樂觀併發,則遊標默認爲基於數值的樂觀併發控制。 SCROLL LOCKS 這個選項實現悲觀併發控制。在悲觀併發控制中,在把數據庫的行讀入遊標結果集時,應用程序將試圖鎖定數據庫行。在使用服務器遊標時,將行讀入遊標時會在其上放置一個更新鎖。若是在事務內打開遊標,則該事務更新鎖將一直保持到事務被提交或回滾;當提取下一行時,將除去遊標鎖。若是在事務外打開遊標,則提取下一行時,鎖就被丟棄。所以,每當用戶須要徹底的悲觀併發控制時,遊標都應在事務內打開。更新鎖將阻止任何其它任務獲取更新鎖或排它鎖,從而阻止其它任務更新該行。然而,更新鎖並不阻止共享鎖,因此它不會阻止其它任務讀取行,除非第二個任務也在要求帶更新鎖的讀取。滾動鎖根據在遊標定義的 Select 語句中指定的鎖提示,這些遊標併發選項能夠生成滾動鎖。滾動鎖在提取時在每行上獲取,並保持到下次提取或者遊標關閉,以先發生者爲準。下次提取時,服務器爲新提取中的行獲取滾動鎖,並釋放上次提取中行的滾動鎖。滾動鎖獨立於事務鎖,並能夠保持到一個提交或回滾操做以後。若是提交時關閉遊標的選項爲關,則 COMMIT 語句並不關閉任何打開的遊標,並且滾動鎖被保留到提交以後,以維護對所提取數據的隔離。所獲取滾動鎖的類型取決於遊標併發選項和遊標 Select 語句中的鎖提示。鎖提示只讀樂觀數值樂觀行版本控制鎖定無提示未鎖定未鎖定未鎖定更新NOLOCK 未鎖定未鎖定未鎖定未鎖定 HOLDLOCK 共享共享共享更新 UPDLOCK 錯誤更新更新更新 TABLOCKX 錯誤未鎖定未鎖定更新其它未鎖定未鎖定未鎖定更新 *指定 NOLOCK 提示將使指定了該提示的表在遊標內是隻讀的。  

  1六、用Profiler來跟蹤查詢,獲得查詢所需的時間,找出SQL的問題所在;用索引優化器優化索引  

  1七、注意UNion和UNion all 的區別。UNION all好  

  1八、注意使用DISTINCT,在沒有必要時不要用,它同UNION同樣會使查詢變慢。重複的記錄在查詢裏是沒有問題的  

  1九、查詢時不要返回不須要的行、列  

  20、用sp_configure 'query governor cost limit'或者SET QUERY_GOVERNOR_COST_LIMIT來限制查詢消耗的資源。當評估查詢消耗的資源超出限制時,服務器自動取消查詢,在查詢以前就扼殺掉。 SET LOCKTIME設置鎖的時間  

  2一、用select top 100 / 10 Percent 來限制用戶返回的行數或者SET ROWCOUNT來限制操做的行  

  2二、在SQL2000之前,通常不要用以下的字句: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'",由於他們不走索引全是表掃描。也不要在Where字句中的列名加函數,如Convert,substring等,若是必須用函數的時候,建立計算列再建立索引來替代.還能夠變通寫法:Where SUBSTRING(firstname,1,1) = 'm'改成Where firstname like 'm%'(索引掃描),必定要將函數和列名分開。而且索引不能建得太多和太大。NOT IN會屢次掃描表,使用EXISTS、NOT EXISTS,IN , LEFT OUTER JOIN 來替代,特別是左鏈接,而Exists比IN更快,最慢的是NOT操做.若是列的值含有空,之前它的索引不起做用,如今2000的優化器可以處理了。相同的是IS NULL,"NOT", "NOT EXISTS", "NOT IN"能優化她,而"<>"等仍是不能優化,用不到索引。  

  2三、使用Query Analyzer,查看SQL語句的查詢計劃和評估分析是不是優化的SQL。通常的20%的代碼佔據了80%的資源,咱們優化的重點是這些慢的地方。  

  2四、若是使用了IN或者OR等時發現查詢沒有走索引,使用顯示申明指定索引: Select * FROM PersonMember (INDEX = IX_Title) Where processid IN ('男','女')  

  2五、將須要查詢的結果預先計算好放在表中,查詢的時候再Select。這在SQL7.0之前是最重要的手段。例如醫院的住院費計算。  

  2六、MIN() 和 MAX()能使用到合適的索引。  

  2七、數據庫有一個原則是代碼離數據越近越好,因此優先選擇Default,依次爲Rules,Triggers, Constraint(約束如外健主健CheckUNIQUE……,數據類型的最大長度等等都是約束),Procedure.這樣不只維護工做小,編寫程序質量高,而且執行的速度快。  

  2八、若是要插入大的二進制值到Image列,使用存儲過程,千萬不要用內嵌Insert來插入(不知JAVA是否)。由於這樣應用程序首先將二進制值轉換成字符串(尺寸是它的兩倍),服務器受到字符後又將他轉換成二進制值.存儲過程就沒有這些動做: 方法:Create procedure p_insert as insert into table(Fimage) values (@image), 在前臺調用這個存儲過程傳入二進制參數,這樣處理速度明顯改善。  

  2九、Between在某些時候比IN 速度更快,Between可以更快地根據索引找到範圍。用查詢優化器可見到差異。 select * from chineseresume where title in ('男','女') Select * from chineseresume where between '男' and '女' 是同樣的。因爲in會在比較屢次,因此有時會慢些。  

  30、在必要是對全局或者局部臨時表建立索引,有時可以提升速度,但不是必定會這樣,由於索引也耗費大量的資源。他的建立同是實際表同樣。  

  3一、不要建沒有做用的事物例如產生報表時,浪費資源。只有在必要使用事物時使用它。  

  3二、用OR的字句能夠分解成多個查詢,而且經過UNION 鏈接多個查詢。他們的速度只同是否使用索引有關,若是查詢須要用到聯合索引,用UNION all執行的效率更高.多個OR的字句沒有用到索引,改寫成UNION的形式再試圖與索引匹配。一個關鍵的問題是否用到索引。  

   3三、儘可能少用視圖,它的效率低。對視圖操做比直接對錶操做慢,能夠用stored procedure來代替她。特別的是不要用視圖嵌套,嵌套視圖增長了尋找原始資料的難度。咱們看視圖的本質:它是存放在服務器上的被優化好了的已經產生了查詢規劃的SQL。對單個表檢索數據時,不要使用指向多個表的視圖,直接從表檢索或者僅僅包含這個表的視圖上讀,不然增長了沒必要要的開銷,查詢受到干擾.爲了加快視圖的查詢,MsSQL增長了視圖索引的功能。  

  3四、沒有必要時不要用DISTINCT和ORDER BY,這些動做能夠改在客戶端執行。它們增長了額外的開銷。這同UNION和UNION ALL同樣的道理。   

  3五、在IN後面值的列表中,將出現最頻繁的值放在最前面,出現得最少的放在最後面,減小判斷的次數。  

  3六、當用Select INTO時,它會鎖住系統表(sysobjects,sysindexes等等),阻塞其餘的鏈接的存取。建立臨時表時用顯示申明語句,而不是 select INTO. drop table t_lxh begin tran select * into t_lxh from chineseresume where name = 'XYZ' --commit 在另外一個鏈接中Select * from sysobjects能夠看到 Select INTO 會鎖住系統表,Create table 也會鎖系統表(無論是臨時表仍是系統表)。因此千萬不要在事物內使用它!!!這樣的話若是是常常要用的臨時表請使用實表,或者臨時表變量。  

  3七、通常在GROUP BY 個HAVING字句以前就能剔除多餘的行,因此儘可能不要用它們來作剔除行的工做。他們的執行順序應該以下最優:select 的Where字句選擇全部合適的行,Group By用來分組個統計行,Having字句用來剔除多餘的分組。這樣Group By個Having的開銷小,查詢快.對於大的數據行進行分組和Having十分消耗資源。若是Group BY的目的不包括計算,只是分組,那麼用Distinct更快  

  3八、一次更新多條記錄比分屢次更新每次一條快,就是說批處理好  

  3九、少用臨時表,儘可能用結果集和Table類性的變量來代替它,Table 類型的變量比臨時表好  

  40、在SQL2000下,計算字段是能夠索引的,須要知足的條件以下:  

  a、計算字段的表達是肯定的  

  b、不能用在TEXT,Ntext,Image數據類型  

  c、必須配製以下選項 ANSI_NULLS = ON, ANSI_PADDINGS = ON, …….  

  4一、儘可能將數據的處理工做放在服務器上,減小網絡的開銷,如使用存儲過程。存儲過程是編譯好、優化過、而且被組織到一個執行規劃裏、且存儲在數據庫中的SQL語句,是控制流語言的集合,速度固然快。反覆執行的動態SQL,能夠使用臨時存儲過程,該過程(臨時表)被放在Tempdb中。之前因爲SQL SERVER對複雜的數學計算不支持,因此不得不將這個工做放在其餘的層上而增長網絡的開銷。SQL2000支持UDFs,如今支持複雜的數學計算,函數的返回值不要太大,這樣的開銷很大。用戶自定義函數象光標同樣執行的消耗大量的資源,若是返回大的結果採用存儲過程  

  4二、不要在一句話裏再三的使用相同的函數,浪費資源,將結果放在變量裏再調用更快  

  4三、Select COUNT(*)的效率教低,儘可能變通他的寫法,而EXISTS快.同時請注意區別: select count(Field of null) from Table和 select count(Field of NOT null) from Table 的返回值是不一樣的!!!  

  4四、當服務器的內存夠多時,配製線程數量 = 最大鏈接數+5,這樣能發揮最大的效率;不然使用配製線程數量<最大鏈接數啓用SQL SERVER的線程池來解決,若是仍是數量 = 最大鏈接數+5,嚴重的損害服務器的性能。  

  4五、按照必定的次序來訪問你的表。若是你先鎖住表A,再鎖住表B,那麼在全部的存儲過程當中都要按照這個順序來鎖定它們。若是你(不經意的)某個存儲過程當中先鎖定表B,再鎖定表A,這可能就會致使一個死鎖。若是鎖定順序沒有被預先詳細的設計好,死鎖很難被發現  

  4六、經過SQL Server Performance Monitor監視相應硬件的負載 Memory: Page Faults / sec計數器若是該值偶爾走高,代表當時有線程競爭內存。若是持續很高,則內存多是瓶頸。

  Process:  

  一、% DPC Time 指在範例間隔期間處理器用在緩延程序調用(DPC)接收和提供服務的百分比。(DPC 正在運行的爲比標準間隔優先權低的間隔)。因爲 DPC 是以特權模式執行的,DPC 時間的百分比爲特權時間百分比的一部分。這些時間單獨計算而且不屬於間隔計算總數的一部分。這個總數顯示了做爲實例時間百分比的平均忙時。  

  二、%Processor Time計數器 若是該參數值持續超過95%,代表瓶頸是CPU。能夠考慮增長一個處理器或換一個更快的處理器。  

  三、% Privileged Time 指非閒置處理器時間用於特權模式的百分比。(特權模式是爲操做系統組件和操縱硬件驅動程序而設計的一種處理模式。它容許直接訪問硬件和全部內存。另外一種模式爲用戶模式,它是一種爲應用程序、環境分系統和整數分系統設計的一種有限處理模式。操做系統將應用程序線程轉換成特權模式以訪問操做系統服務)。特權時間的 % 包括爲間斷和 DPC 提供服務的時間。特權時間比率高多是因爲失敗設備產生的大數量的間隔而引發的。這個計數器將平均忙時做爲樣本時間的一部分顯示。  

  四、% User Time表示耗費CPU的數據庫操做,如排序,執行aggregate functions等。若是該值很高,可考慮增長索引,儘可能使用簡單的表聯接,水平分割大表格等方法來下降該值。 Physical Disk: Curretn Disk Queue Length計數器該值應不超過磁盤數的1.5~2倍。要提升性能,可增長磁盤。 SQLServer:Cache Hit Ratio計數器該值越高越好。若是持續低於80%,應考慮增長內存。注意該參數值是從SQL Server啓動後,就一直累加記數,因此運行通過一段時間後,該值將不能反映系統當前值。  

  4七、分析select emp_name form employee where salary > 3000 在此語句中若salary是Float類型的,則優化器對其進行優化爲Convert(float,3000),由於3000是個整數,咱們應在編程時使用3000.0而不要等運行時讓DBMS進行轉化。一樣字符和整型數據的轉換。  

  4八、查詢的關聯同寫的順序  

  select a.personMemberID, * from chineseresume a,personmember b where personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' (A = B ,B = '號碼')  

  select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' and b.referenceid = 'JCNPRH39681' (A = B ,B = '號碼', A = '號碼')  

  select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = 'JCNPRH39681' and a.personMemberID = 'JCNPRH39681' (B = '號碼', A = '號碼')  

  4九、  

  (1)IF 沒有輸入負責人代碼 THEN code1=0 code2=9999 ELSE code1=code2=負責人代碼 END IF 執行SQL語句爲: Select 負責人名 FROM P2000 Where 負責人代碼>=:code1 AND負責人代碼 <=:code2  

  (2)IF 沒有輸入負責人代碼 THEN  Select 負責人名 FROM P2000 ELSE code= 負責人代碼 Select 負責人代碼 FROM P2000 Where 負責人代碼=:code END IF 第一種方法只用了一條SQL語句,第二種方法用了兩條SQL語句。在沒有輸入負責人代碼時,第二種方法顯然比第一種方法執行效率高,由於它沒有限制條件; 在輸入了負責人代碼時,第二種方法仍然比第一種方法效率高,不只是少了一個限制條件,還因相等運算是最快的查詢運算。咱們寫程序不要怕麻煩  

  50、關於JOBCN如今查詢分頁的新方法(以下),用性能優化器分析性能的瓶頸,若是在I/O或者網絡的速度上,以下的方法優化切實有效,若是在CPU或者內存上,用如今的方法更好。請區分以下的方法,說明索引越小越好。  

  begin  

  DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))  

  insert into @local_variable (ReferenceID)  

  select top 100000 ReferenceID from chineseresume order by ReferenceID  

  select * from @local_variable where Fid > 40 and fid <= 60  

  end 和

  begin  

  DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))  

  insert into @local_variable (ReferenceID)  

  select top 100000 ReferenceID from chineseresume order by updatedate  

  select * from @local_variable where Fid > 40 and fid <= 60  

  end 的不一樣

  begin  

  create table #temp (FID int identity(1,1),ReferenceID varchar(20))  

  insert into #temp (ReferenceID)  

  select top 100000 ReferenceID from chineseresume order by updatedate  

  select * from #temp where Fid > 40 and fid <= 60 drop table #temp  

  end

徹底經過系統級服務器性能優化(如內存大小、文件系統類型、處理器的數目及類型等)解決性能問題可能很誘人。但經驗代表大多數性能問題不能用這種方法解決。必須經過這些方法解決性能問題:分析應用程序以及應用程序提交給數據庫的查詢和更新,並分析這些查詢和更新如何與數據庫架構交互。

持續時間意外地長的查詢和更新可能由下列緣由引發:

· 網絡通信速度慢。

· 服務器計算機的內存不足或 Microsoft? SQL Server? 2000 可用的內存不足。

· 缺乏有用的統計數據。

· 統計數據過時。

· 缺乏有用的索引

· 缺乏有用的數據條帶化。

當查詢或更新花費的時間比預期的長時,使用下面的檢查清單提升性能:

說明  建議在與技術支持提供商聯繫以前先參考該檢查清單。

1. 性能問題與查詢之外的組件是否有關?例如,問題是否爲網絡性能慢?是否有任何其它可能引發或間接致使性能降低的組件?能夠使用 Windows NT 性能監視器監視與 SQL Server 相關和與 SQL Server 不相關的組件性能。有關更多信息,請參見使用系統監視器進行監視。

2. 若是性能問題與查詢相關,涉及哪一個查詢或哪組查詢?使用 SQL 事件探查器幫助識別慢速查詢。有關更多信息,請參見使用 SQL 事件探查器進行監視。

經過使用 SET 語句啓用 SHOWPLAN、STATISTICS IO、STATISTICS TIME 和 STATISTICS PROFILE 選項,能夠肯定數據庫查詢性能。

·SHOWPLAN 描述 SQL Server 查詢優化器選擇的數據檢索方法。有關更多信息,請參見 SET SHOWPLAN_ALL。

·STATISTICS IO 報告與語句內引用的每一個表的掃描數、邏輯讀取數(在高速緩存中訪問的頁數)和物理讀取數(訪問磁盤的次數)有關的信息。有關更多信息,請參見 SET STATISTICS IO。

· STATISTICS TIME 顯示分析、編譯和執行查詢所需的時間(以毫秒爲單位)。有關更多信息,請參見 SET STATISTICS TIME。

·STATISTICS PROFILE 顯示每一個查詢執行後的結果集,表明查詢執行的配置文件。有關更多信息,請參見SET STATISTICS PROFILE。

在 SQL 查詢分析器中,還能夠打開 graphical execution plan 選項查看關於 SQL Server 如何檢索數據的圖形表示。

由這些工具收集的信息使您得以肯定 SQL Server 查詢優化器正在如何執行查詢以及正在使用哪些索引。利用這些信息,能夠肯定經過重寫查詢、更改表上的索引或修改數據庫設計等方法可否提升性能。有關更多信息,請參見分析查詢。

3.是否已經用有用的統計數據優化查詢?

SQL Server 自動在索引列上建立對列內的值分佈狀況的統計。也能夠使用 SQL 查詢分析器或 CREATE STATISTICS 語句在非索引列上手動建立統計;或者若是將 auto create statistics 數據庫選項設置爲 true,則自動在非索引列上建立統計。查詢處理器能夠利用這些統計肯定最佳的查詢評估策略。在聯接操做所涉及的非索引列上維護附加的統計信息能夠提升查詢性能。有關更多信息,請參見統計信息。

使用 SQL 事件探查器或 SQL 查詢分析器內的圖形執行計劃來監視查詢,以肯定查詢是否有足夠的統計信息。有關更多信息,請參見錯誤和警告事件分類。

4.查詢統計信息是否爲最新?統計信息是否自動更新?

SQL Server 自動在索引列上建立並更新查詢統計(只要沒有禁用自動查詢統計更新特性)。另外,能夠使用 SQL 查詢分析器或 UPDATE STATISTICS 語句在非索引列上手工更新統計;或者若是 auto update statistics 數據庫選項設置爲 true,則自動在非索引列上更新統計。最新的統計不取決於日期或時間數據。若是還沒有進行 UPDATE 操做,則查詢統計信息還是最新的。

若是沒有將統計設置爲自動更新,則應設置爲自動更新。有關更多信息,請參見統計信息。

5.是否有合適的索引?添加一個或多個索引是否會提升查詢性能?有關更多信息,請參見索引優化建議。

6.是否有任何數據熱點或索引熱點?若是有,考慮使用磁盤條帶化。有關更多信息,請參見使用文件組放置數據和 RAID。

7.是否爲查詢優化器提供了優化複雜查詢的最有利條件?有關更多信息,請參見查詢優化建議。

存儲過程的優化

1、前言:在通過一段時間的存儲過程開發以後,寫下了一些開發時候的小結和經驗與你們共享,但願對你們有益,主要是針對Sybase和SQL Server數據庫,但其它數據庫應該有一些共性。

2、適合讀者對象:數據庫開發程序員,數據庫的數據量不少,涉及到對SP(存儲過程)的優化的項目開發人員,對數據庫有濃厚興趣的人。

3、介紹:在數據庫的開發過程當中,常常會遇到複雜的業務邏輯和對數據庫的操做,這個時候就會用SP來封裝數據庫操做。若是項目的SP較多,書寫又沒有必定的規範,將會影響之後的系統維護困難和大SP邏輯的難以理解,另外若是數據庫的數據量大或者項目對SP的性能要求很,就會遇到優化的問題,不然速度有可能很慢,通過親身經驗,一個通過優化過的SP要比一個性能差的SP的效率甚至高几百倍。

4、內容:

一、開發人員若是用到其餘庫的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 等。

如下是一些經常使用的優化須要注意的方面:

操做符優化

IN 操做符

用IN寫出來的SQL的優勢是比較容易寫及清晰易懂,這比較適合現代軟件開發的風格。

可是用IN的SQL性能老是比較低的,從ORACLE執行的步驟來分析用IN的SQL與不用IN的SQL有如下區別:

ORACLE試圖將其轉換成多個表的鏈接,若是轉換不成功則先執行IN裏面的子查詢,再查詢外層的表記錄,若是轉換成功則直接採用多個表的鏈接方式查詢。因而可知用IN的SQL至少多了一個轉換的過程。通常的SQL均可以轉換成功,但對於含有分組統計等方面的SQL就不能轉換了。

推薦方案:在業務密集的SQL當中儘可能不採用IN操做符。

NOT IN操做符

此操做是強列推薦不使用的,由於它不能應用表的索引。

推薦方案:用NOT EXISTS 或(外鏈接+判斷爲空)方案代替

<> 操做符(不等於)

不等於操做符是永遠不會用到索引的,所以對它的處理只會產生全表掃描。

推薦方案:用其它相同功能的操做運算代替,如

a<>0 改成 a>0 or a<0

a<>’’ 改成 a>’’

IS NULL 或IS NOT NULL操做(判斷字段是否爲空)

判斷字段是否爲空通常是不會應用索引的,由於B樹索引是不索引空值的。

推薦方案:

用其它相同功能的操做運算代替,如

a is not null 改成 a>0 或a>’’等。

不容許字段爲空,而用一個缺省值代替空值,如業擴申請中狀態字段不容許爲空,缺省爲申請。

創建位圖索引(有分區的表不能建,位圖索引比較難控制,如字段值太多索引會使性能降低,多人更新操做會增長數據塊鎖的現象)

> 及 < 操做符(大於或小於操做符)

大於或小於操做符通常狀況下是不用調整的,由於它有索引就會採用索引查找,但有的狀況下能夠對它進行優化,如一個表有100萬記錄,一個數值型字段A,30萬記錄的A=0,30萬記錄的A=1,39萬記錄的A=2,1萬記錄的A=3。那麼執行A>2與A>=3的效果就有很大的區別了,由於A>2時ORACLE會先找出爲2的記錄索引再進行比較,而A>=3時ORACLE則直接找到=3的記錄索引。

LIKE操做符

LIKE操做符能夠應用通配符查詢,裏面的通配符組合可能達到幾乎是任意的查詢,可是若是用得很差則會產生性能上的問題,如LIKE ‘%5400%’ 這種查詢不會引用索引,而LIKE ‘X5400%’則會引用範圍索引。一個實際例子:用YW_YHJBQK表中營業編號後面的戶標識號可來查詢營業編號 YY_BH LIKE ‘%5400%’ 這個條件會產生全表掃描,若是改爲YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 則會利用YY_BH的索引進行兩個範圍的查詢,性能確定大大提升。

UNION操做符

UNION在進行表連接後會篩選掉重複的記錄,因此在表連接後會對所產生的結果集進行排序運算,刪除重複的記錄再返回結果。實際大部分應用中是不會產生重複的記錄,最多見的是過程表與歷史表UNION。如:

select * from gc_dfys

union

select * from ls_jg_dfys

這個SQL在運行時先取出兩個表的結果,再用排序空間進行排序刪除重複的記錄,最後返回結果集,若是表數據量大的話可能會致使用磁盤進行排序。

推薦方案:採用UNION ALL操做符替代UNION,由於UNION ALL操做只是簡單的將兩個結果合併後就返回。

select * from gc_dfys

union all

select * from ls_jg_dfys

SQL書寫的影響

同一功能同一性能不一樣寫法SQL的影響

如一個SQL在A程序員寫的爲

Select * from zl_yhjbqk

B程序員寫的爲

Select * from dlyx.zl_yhjbqk(帶表全部者的前綴)

C程序員寫的爲

Select * from DLYX.ZLYHJBQK(大寫表名)

D程序員寫的爲

Select * from DLYX.ZLYHJBQK(中間多了空格)

以上四個SQL在ORACLE分析整理以後產生的結果及執行的時間是同樣的,可是從ORACLE共享內存SGA的原理,能夠得出ORACLE對每一個SQL 都會對其進行一次分析,而且佔用共享內存,若是將SQL的字符串及格式寫得徹底相同則ORACLE只會分析一次,共享內存也只會留下一次的分析結果,這不只能夠減小分析SQL的時間,並且能夠減小共享內存重複的信息,ORACLE也能夠準確統計SQL的執行頻率。

WHERE後面的條件順序影響

WHERE子句後面的條件順序對大數據量表的查詢會產生直接的影響,如

Select * from zl_yhjbqk where dy_dj = '1KV如下' and xh_bz=1

Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV如下'

以上兩個SQL中dy_dj(電壓等級)及xh_bz(銷戶標誌)兩個字段都沒進行索引,因此執行的時候都是全表掃描,第一條SQL的dy_dj = '1KV如下'條件在記錄集內比率爲99%,而xh_bz=1的比率只爲0.5%,在進行第一條SQL的時候99%條記錄都進行dy_dj及xh_bz的比較,而在進行第二條SQL的時候0.5%條記錄都進行dy_dj及xh_bz的比較,以此能夠得出第二條SQL的CPU佔用率明顯比第一條低。

查詢表順序的影響

在FROM後面的表中的列表順序會對SQL執行性能影響,在沒有索引及ORACLE沒有對錶進行統計分析的狀況下ORACLE會按表出現的順序進行連接,由此由於表的順序不對會產生十分耗服務器資源的數據交叉。(注:若是對錶進行了統計分析,ORACLE會自動先進小表的連接,再進行大表的連接)

SQL語句索引的利用

對操做符的優化(見上節)

對條件字段的一些優化

採用函數處理的字段不能利用索引,如:

substr(hbs_bh,1,4)=’5400’,優化處理:hbs_bh like ‘5400%’

trunc(sk_rq)=trunc(sysdate),優化處理:

sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)

進行了顯式或隱式的運算的字段不能進行索引,如:

ss_df+20>50,優化處理:ss_df>30

‘X’||hbs_bh>’X5400021452’,優化處理:hbs_bh>’5400021542’

sk_rq+5=sysdate,優化處理:sk_rq=sysdate-5

hbs_bh=5401002554,優化處理:hbs_bh=’ 5401002554’,注:此條件對hbs_bh 進行隱式的to_number轉換,由於hbs_bh字段是字符型。

條件內包括了多個本表的字段運算時不能進行索引,如:

ys_df>cx_df,沒法進行優化

qc_bh||kh_bh=’5400250000’,優化處理:qc_bh=’5400’ and kh_bh=’250000’

應用ORACLE的HINT(提示)處理

提示處理是在ORACLE產生的SQL分析執行路徑不滿意的狀況下要用到的。它能夠對SQL進行如下方面的提示

目標方面的提示:

COST(按成本優化)

RULE(按規則優化)

CHOOSE(缺省)(ORACLE自動選擇成本或規則進行優化)

ALL_ROWS(全部的行儘快返回)

FIRST_ROWS(第一行數據儘快返回)

執行方法的提示:

USE_NL(使用NESTED LOOPS方式聯合)

USE_MERGE(使用MERGE JOIN方式聯合)

USE_HASH(使用HASH JOIN方式聯合)

索引提示:

INDEX(TABLE INDEX)(使用提示的表索引進行查詢)

其它高級提示(如並行處理等等)

ORACLE的提示功能是比較強的功能,也是比較複雜的應用,而且提示只是給ORACLE執行的一個建議,有時若是出於成本方面的考慮ORACLE也可能不會按提示進行。根據實踐應用,通常不建議開發人員應用ORACLE提示,由於各個數據庫及服務器性能狀況不同,極可能一個地方性能提高了,但另外一個地方卻降低了,ORACLE在SQL執行分析方面已經比較成熟,若是分析執行的路徑不對首先應在數據庫結構(主要是索引)、服務器當前性能(共享內存、磁盤文件碎片)、數據庫對象(表、索引)統計信息是否正確這幾方面分析。

與沒有優化數據庫的網站相比,數據庫的存取會下降你的系統性能。可是大多數狀況下,網站和數據庫有密不可分的關係,正是數據庫給站點提供了大容量、多樣性、個性化等特點,並實現了不少特殊的功能。

1 不要忘記給數據庫作索引。

合理的索引能當即顯著地提升數據庫整個系統的性能。能夠參考有關SQL性能調試書籍,學會根據所需查詢方式合理製做索引和根據索引方式改進查詢語句。

2 在適當的狀況下,儘量的用存儲過程而不是SQL查詢。

由於前者已通過了預編譯,運行速度更快。同時讓數據庫僅僅返回你所須要的那些數據,而不是返回大量數據再讓ASP程序過濾。總之要充分和有效地發揮數據庫的強大功能,讓它按照咱們的要求反饋給咱們最合適和最精練的信息。

3 在可能狀況下咱們應該使用SQL Server而不是Access。由於Access僅僅是基於文件的數據庫,多用戶性能不好。數據庫鏈接儘可能使用OLEDB和非DSN方式,由於這種鏈接方式有更好的併發性能。

4 避免使用DAO(Data Access Objects)和RDO(Remote Data Objects)數據源。由於他們主要應用在單用戶的處理系統裏,ADO(ActiveX Data Objects)纔是爲Web應用設計的。

5 創建記錄集Rescordset的時候要清晰合理地設置數據遊標(cursort)和鎖定方式(locktype)。

由於在不一樣的方式下ASP會以不一樣的方式操縱數據庫,其執行速度也有很大區別,尤爲在大數據量的時候。若是你只想遍歷數據,那麼默認遊標(前進、只讀)會帶來最好的性能。

6 當你引用ADO變量的時候,會消耗較多的CPU週期。所以,若是在一個ASP頁面中屢次引用數據庫的字段變量,一個較好的方式是將字段值先放入本地變量,而後能夠直接調用本地變量來計算和顯示數據。

7 緩存ADO Connection對象也許不是一個好主意。

若是一個鏈接(Connection)對象被存儲在Application對象中而被全部ASP頁面使用,那麼全部頁面就會爭着使用這個鏈接。可是若是鏈接對象被存儲在Session對象中,就要爲每一個用戶建立一個數據庫鏈接,這就減少了鏈接池的做用,而且增大了Web服務器和數據庫服務器的壓力。能夠用在每一個使用ADO的ASP頁建立和釋放ADO對象來替代緩存數據庫鏈接。由於IIS內建了數據庫鏈接池,因此這種方法很是有效,缺點是每一個ASP頁面都須要進行一些建立和釋放操做。

8 ASP最強大和主要的用途之一就是對數據庫進行操做,在數據庫操做中咱們要注意:不要任意使用「SELECT * ......」 形式的SQL查詢語句。應該儘可能檢索你所須要的那些字段。好比一個表中有10個字段,可是你只會用到其中的一個字段(name),就該使用「select name from mytable」,而不是用「select * from mytable」。在字段數比較少的時候,二者的區別可能並不明顯,可是當一個表中擁有幾十個字段的時候,數據庫會多檢索不少你並不須要的數據。在這種狀況下你最好不要爲了節省打字時間或者懼怕查找對應字段名稱的麻煩,而要老老實實地使用「select id,name,age... from mytable」。

9 及時關閉打開的記錄集對象以及鏈接(Connection)對象。

記錄集對象和鏈接對象耗費系統資源至關大,所以它們的可用數量是有限的。若是你打開了太多的記錄集對象以及鏈接對象而最後卻沒有關閉它們,可能會出現ASP程序剛開始的時候運行速度很快,而多運行幾遍就愈來愈慢的現象,甚至致使服務器死機。請使用以下方法進行關閉:

MyRecordSet.closeSet MyRecordSet=Nothing

Set MyConnection=Nothing

10 鏈接數據庫

仍然使用ODBC系統或者文件DSN來鏈接數據庫,或者使用很快的OLEDB技術來鏈接。使用後者,當移動Web文件時,再也不須要修改配置。

OLEDB位於應用程序與ODBC層之間。在ASP頁面中,ADO就是位於OLEDB之上的程序。調用ADO時,首先發送給OLEDB,而後再發送給ODBC層。能夠直接鏈接到OLEDB層,這麼作後,將提升服務器端的性能。怎麼直接鏈接到OLEDB呢?

若是使用SQLServer 7,使用下面的代碼作爲鏈接字符串:

strConnString = "DSN='';DRIVER={SQL SERVER};" & _

"UID=myuid;PWD=mypwd;" & _

"DATABASE=MyDb;SERVER=MyServer;"

最重要的參數就是「DRIVER=」部分。若是你想繞過ODBC而使用OLEDB來訪問SQL Server,使用下面的語法:

strConnString ="Provider=SQLOLEDB.1;Password=mypassword;" & _

"Persist Security Info=True;User ID=myuid;" & _

"Initial Catalog=mydbname;" & _

"Data Source=myserver;Connect Timeout=15"

爲何這很重要

如今你可能奇怪爲何學習這種新的鏈接方法很關鍵?爲何不使用標準的DSN或者系統DSN方法?好,根據Wrox在他們的ADO 2.0程序員參考書籍中所作的測試,若是使用OLEDB鏈接,要比使用DSN或者DSN-less鏈接,有如下的性能提升表現:

性能比較:

----------------------------------------------------------------------

SQL Access

鏈接時間: 18 82

重複1,000個記錄的時間:2900 5400

OLEDB DSN OLEDB DSN

鏈接時間:62 99

重複1,000個記錄的時間:100 950

----------------------------------------------------------------------

這個結論在Wrox的ADO 2.0程序員參考發表。時間是以毫秒爲單位,重複1,000個記錄的時間是以服務器油標的方式計算的。

有一個例子:

select a. *, m.amount

from tableA a,

(

select b.fieldD, sum(c.total_amount) amount

from tableA b, tableB c

where b.fieldC = 100 and

b.fieldA in ('AA', 'BB', 'CC', 'DD', 'EE', 'FF') and

b.fieldId = c.fieldId

group by b.fieldD

) m

where a.fieldC = 100 and a.fieldD = m.fieldD and

a.fieldA = 'GG'

這句sql當中對同一個表掃描了兩次,因此效率過低,有什麼辦法能夠避免這種寫法?

tableA,tableB 是主從表關係。

請不要用sql server 中太特殊的語法,由於要用到oracle中。

在oracle中無人回答。

------------------------------------------

SQL語句的寫法是根據你的業務要求,改寫起來效果不能很明顯。

先分析一下你的SQL的執行路徑:

一、

首先會分別對tableA和tableB應用filter動做(使用m子查詢中的where條件)。而後進行鏈接,可能會是nestloop或hash join...這取決於你的兩個表數據過濾狀況。而後進行彙總(group by)輸出m結果集。

二、接下來會將m結果集與tableA(外層)過濾後(a.fieldC = 100 and a.fieldA = 'GG')的結果集進行鏈接,仍是有多種鏈接方式。最後輸出a. *, m.amount。大體分析了一下執行的路徑,就會對你的描述產生疑惑:「對同一個表掃描了兩次」確定指的是tableA了。可是你沒有創建相關的索引嗎?若是說外層的查詢就算創建索引也會經過rowid定位到表中,咱們權當這是「表掃描」,可是內層的查詢應該不會發生產生表掃描(all table access)的狀況!應該是索引掃描(index scan)纔對。根據這一點,咱們能夠首先考慮創建索引來提升效率。

能夠考慮創建的索引:

create index idx_1 on tableA(fieldC,fieldA,fieldId,fieldD)

create index idx_2 on tableB(fieldId,total_amount)

創建完這兩個索引後別忘了從新執行分析,以保證統計值準確。

創建完這兩個索引後,內層的執行計劃應該是對idx_1和idx_2進行索引掃描(index scan)而後鏈接輸出m結果集,再與外層的通過索引掃描(index scan + rowid to table)的結果集進行鏈接。若是查詢計劃不對,請檢查你的優化器參數設置,不要使用rbo要使用cbo。若是仍是沒有采用請用/* index*/提示強制指定....

上面的是單純從索引方面考慮。若是仍是不能提升速度,考慮創建實體化視圖(物化視圖)。能夠只將m部分進行實體化。若是tableA和tableB基本屬於靜態表,能夠考慮將整條語句實體化。

這裏有個很是好的例子並總結了:

SERVER數據庫中實現快速的數據提取和數據分頁。如下代碼說明了咱們實例中數據庫的「紅頭文件」一表的部分數據結構:

CREATE table [dbo].[TGongwen] (  --TGongwen是紅頭文件表名

[Gid] [int] ideNTITY (1, 1) NOT NULL ,

--本表的id號,也是主鍵

[title] [varchar] (80) COLLATE Chinese_PRC_CI_AS NULL ,

--紅頭文件的標題

[fariqi] [datetime] NULL ,

--發佈日期

[neibuYonghu] [varchar] (70) COLLATE Chinese_PRC_CI_AS NULL ,

--發佈用戶

[reader] [varchar] (900) COLLATE Chinese_PRC_CI_AS NULL ,

--須要瀏覽的用戶。每一個用戶中間用分隔符「,」分開

) ON [PRIMARY] TEXTimage_ON [PRIMARY]

GO

下面,咱們來往數據庫中添加1000萬條數據:

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-2-5','通訊科','通訊科,辦公室,王局長,劉局長,張局長,admin,刑偵支隊,特勤支隊,交巡警支隊,經偵支隊,戶政科,治安支隊,外事科','這是最早的25萬條記錄')

set @i=@i+1

end

GO

declare @i int

set @i=1

while @i<=250000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-9-16','辦公室','辦公室,通訊科,王局長,劉局長,張局長,admin,刑偵支隊,特勤支隊,交巡警支隊,經偵支隊,戶政科,外事科','這是中間的25萬條記錄')

set @i=@i+1

end

GO

declare @h int

set @h=1

while @h<=100

begin

declare @i int

set @i=2002

while @i<=2003

begin

declare @j int

set @j=0

while @j<50

begin

declare @k int

set @k=0

while @k<50

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values(cast(@i as varchar(4))+'-8-15 3:'+cast(@j as varchar(2))+':'+cast(@j as varchar(2)),'通訊科','辦公室,通訊科,王局長,劉局長,張局長,admin,刑偵支隊,特勤支隊,交巡警支隊,經偵支隊,戶政科,外事科','這是最後的50萬條記錄')

set @k=@k+1

end

set @j=@j+1

end

set @i=@i+1

end

set @h=@h+1

end

GO

declare @i int

set @i=1

while @i<=9000000

begin

insert into Tgongwen(fariqi,neibuyonghu,reader,title) values('2004-5-5','通訊科','通訊科,辦公室,王局長,劉局長,張局長,admin,刑偵支隊,特勤支隊,交巡警支隊,經偵支隊,戶政科,治安支隊,外事科','這是最後添加的900萬條記錄')

set @i=@i+1000000

end

GO

經過以上語句,咱們建立了25萬條因爲2004年2月5日發佈的記錄,25萬條由辦公室於2004年9月6日發佈的記錄,2002年和2003年各100個2500條相同日期、不一樣分秒的記錄(共50萬條),還有由通訊科於2004年5月5日發佈的900萬條記錄,合計1000萬條。

1、因情制宜,創建「適當」的索引

創建「適當」的索引是實現查詢優化的首要前提。

索引(index)是除表以外另外一重要的、用戶定義的存儲在物理介質上的數據結構。當根據索引碼的值搜索數據時,索引提供了對數據的快速訪問。事實上,沒有索引,數據庫也能根據select語句成功地檢索到結果,但隨着表變得愈來愈大,使用「適當」的索引的效果就愈來愈明顯。注意,在這句話中,咱們用了「適當」這個詞,這是由於,若是使用索引時不認真考慮其實現過程,索引既能夠提升也會破壞數據庫的工做性能。

(一)深刻淺出理解索引結構

實際上,您能夠把索引理解爲一種特殊的目錄。微軟的SQL SERVER提供了兩種索引:彙集索引(clustered index,也稱聚類索引、簇集索引)和非彙集索引(nonclustered index,也稱非聚類索引、非簇集索引)。下面,咱們舉例來講明一下彙集索引和非彙集索引的區別:

其實,咱們的漢語字典的正文自己就是一個彙集索引。好比,咱們要查「安」字,就會很天然地翻開字典的前幾頁,由於「安」的拼音是「an」,而按照拼音排序漢字的字典是以英文字母「a」開頭並以「z」結尾的,那麼「安」字就天然地排在字典的前部。若是您翻完了全部以「a」開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;一樣的,若是查「張」字,那您也會將您的字典翻到最後部分,由於「張」的拼音是「zhang」。也就是說,字典的正文部分自己就是一個目錄,您不須要再去查其餘目錄來找到您須要找的內容。

咱們把這種正文內容自己就是一種按照必定規則排列的目錄稱爲「彙集索引」。

若是您認識某個字,您能夠快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛纔的方法找到您要查的字,而須要去根據「偏旁部首」查到您要找的字,而後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合「部首目錄」和「檢字表」而查到的字的排序並非真正的正文的排序方法,好比您查「張」字,咱們能夠看到在查部首以後的檢字表中「張」的頁碼是672頁,檢字表中「張」的上面是「馳」字,但頁碼倒是63頁,「張」的下面是「弩」字,頁面是390頁。很顯然,這些字並非真正的分別位於「張」字的上下方,如今您看到的連續的「馳、張、弩」三字實際上就是他們在非彙集索引中的排序,是字典正文中的字在非彙集索引中的映射。咱們能夠經過這種方式來找到您所須要的字,但它須要兩個過程,先找到目錄中的結果,而後再翻到您所須要的頁碼。

咱們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱爲「非彙集索引」。

經過以上例子,咱們能夠理解到什麼是「彙集索引」和「非彙集索引」。

進一步引伸一下,咱們能夠很容易的理解:每一個表只能有一個彙集索引,由於目錄只能按照一種方法進行排序。

(二)什麼時候使用匯集索引或非彙集索引

下面的表總結了什麼時候使用匯集索引或非彙集索引(很重要)。

動做描述

使用匯集索引

使用非彙集索引

列常常被分組排序

返回某範圍內的數據

不該

一個或極少不一樣值

不該

不該

小數目的不一樣值

不該

大數目的不一樣值

不該

頻繁更新的列

不該

外鍵列

主鍵列

頻繁修改索引列

不該

事實上,咱們能夠經過前面彙集索引和非彙集索引的定義的例子來理解上表。如:返回某範圍內的數據一項。好比您的某個表有一個時間列,剛好您把聚合索引創建在了該列,這時您查詢2004年1月1日至2004年10月1日之間的所有數據時,這個速度就將是很快的,由於您的這本字典正文是按日期進行排序的,聚類索引只須要找到要檢索的全部數據中的開頭和結尾數據便可;而不像非彙集索引,必須先查到目錄中查到每一項數據對應的頁碼,而後再根據頁碼查到具體內容。

(三)結合實際,談索引使用的誤區

理論的目的是應用。雖然咱們剛纔列出了什麼時候應使用匯集索引或非彙集索引,但在實踐中以上規則卻很容易被忽視或不能根據實際狀況進行綜合分析。下面咱們將根據在實踐中遇到的實際問題來談一下索引使用的誤區,以便於你們掌握索引創建的方法。

一、主鍵就是彙集索引

這種想法筆者認爲是極端錯誤的,是對彙集索引的一種浪費。雖然SQL SERVER默認是在主鍵上創建彙集索引的。

一般,咱們會在每一個表中都創建一個ID列,以區分每條數據,而且這個ID列是自動增大的,步長通常爲1。咱們的這個辦公自動化的實例中的列Gid就是如此。此時,若是咱們將這個列設爲主鍵,SQL SERVER會將此列默認爲彙集索引。這樣作有好處,就是可讓您的數據在數據庫中按照ID進行物理排序,但筆者認爲這樣作意義不大。

顯而易見,彙集索引的優點是很明顯的,而每一個表中只能有一個彙集索引的規則,這使得彙集索引變得更加珍貴。

從咱們前面談到的彙集索引的定義咱們能夠看出,使用匯集索引的最大好處就是可以根據查詢要求,迅速縮小查詢範圍,避免全表掃描。在實際應用中,由於ID號是自動生成的,咱們並不知道每條記錄的ID號,因此咱們很難在實踐中用ID號來進行查詢。這就使讓ID號這個主鍵做爲彙集索引成爲一種資源浪費。其次,讓每一個ID號都不一樣的字段做爲彙集索引也不符合「大數目的不一樣值狀況下不該創建聚合索引」規則;固然,這種狀況只是針對用戶常常修改記錄內容,特別是索引項的時候會負做用,但對於查詢速度並無影響。

在辦公自動化系統中,不管是系統首頁顯示的須要用戶簽收的文件、會議仍是用戶進行文件查詢等任何狀況下進行數據查詢都離不開字段的是「日期」還有用戶自己的「用戶名」。

一般,辦公自動化的首頁會顯示每一個用戶還沒有簽收的文件或會議。雖然咱們的where語句能夠僅僅限制當前用戶還沒有簽收的狀況,但若是您的系統已創建了很長時間,而且數據量很大,那麼,每次每一個用戶打開首頁的時候都進行一次全表掃描,這樣作意義是不大的,絕大多數的用戶1個月前的文件都已經瀏覽過了,這樣作只能徒增數據庫的開銷而已。事實上,咱們徹底可讓用戶打開系統首頁時,數據庫僅僅查詢這個用戶近3個月來未閱覽的文件,經過「日期」這個字段來限制表掃描,提升查詢速度。若是您的辦公自動化系統已經創建的2年,那麼您的首頁顯示速度理論上將是原來速度8倍,甚至更快。

在這裏之因此提到「理論上」三字,是由於若是您的彙集索引仍是盲目地建在ID這個主鍵上時,您的查詢速度是沒有這麼高的,即便您在「日期」這個字段上創建的索引(非聚合索引)。下面咱們就來看一下在1000萬條數據量的狀況下各類查詢的速度表現(3個月內的數據爲25萬條):

(1)僅在主鍵上創建彙集索引,而且不劃分時間段:

Select gid,fariqi,neibuyonghu,title from tgongwen

用時:128470毫秒(即:128秒)

(2)在主鍵上創建彙集索引,在fariq上創建非彙集索引:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用時:53763毫秒(54秒)

(3)將聚合索引創建在日期列(fariqi)上:

select gid,fariqi,neibuyonghu,title from Tgongwen

where fariqi> dateadd(day,-90,getdate())

用時:2423毫秒(2秒)

雖然每條語句提取出來的都是25萬條數據,各類狀況的差別倒是巨大的,特別是將彙集索引創建在日期列時的差別。事實上,若是您的數據庫真的有1000萬容量的話,把主鍵創建在ID列上,就像以上的第一、2種狀況,在網頁上的表現就是超時,根本就沒法顯示。這也是我摒棄ID列做爲彙集索引的一個最重要的因素。

得出以上速度的方法是:在各個select語句前加:declare @d datetime

set @d=getdate()

並在select語句後加:

select [語句執行花費時間(毫秒)]=datediff(ms,@d,getdate())

二、只要創建索引就能顯著提升查詢速度

事實上,咱們能夠發現上面的例子中,第二、3條語句徹底相同,且創建索引的字段也相同;不一樣的僅是前者在fariqi字段上創建的是非聚合索引,後者在此字段上創建的是聚合索引,但查詢速度卻有着天壤之別。因此,並不是是在任何字段上簡單地創建索引就能提升查詢速度。

從建表的語句中,咱們能夠看到這個有着1000萬數據的表中fariqi字段有5003個不一樣記錄。在此字段上創建聚合索引是再合適不過了。在現實中,咱們天天都會發幾個文件,這幾個文件的發文日期就相同,這徹底符合創建彙集索引要求的:「既不能絕大多數都相同,又不能只有極少數相同」的規則。由此看來,咱們創建「適當」的聚合索引對於咱們提升查詢速度是很是重要的。

三、把全部須要提升查詢速度的字段都加進彙集索引,以提升查詢速度

上面已經談到:在進行數據查詢時都離不開字段的是「日期」還有用戶自己的「用戶名」。既然這兩個字段都是如此的重要,咱們能夠把他們合併起來,創建一個複合索引(compound index)。

不少人認爲只要把任何字段加進彙集索引,就能提升查詢速度,也有人感到迷惑:若是把複合的彙集索引字段分開查詢,那麼查詢速度會減慢嗎?帶着這個問題,咱們來看一下如下的查詢速度(結果集都是25萬條數據):(日期列fariqi首先排在複合彙集索引的起始列,用戶名neibuyonghu排在後列)

(1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5'

查詢速度:2513毫秒

(2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' and neibuyonghu='辦公室'

查詢速度:2516毫秒

(3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='辦公室'

查詢速度:60280毫秒

從以上試驗中,咱們能夠看到若是僅用匯集索引的起始列做爲查詢條件和同時用到複合彙集索引的所有列的查詢速度是幾乎同樣的,甚至比用上所有的複合索引列還要略快(在查詢結果集數目同樣的狀況下);而若是僅用複合彙集索引的非起始列做爲查詢條件的話,這個索引是不起任何做用的。固然,語句一、2的查詢速度同樣是由於查詢的條目數同樣,若是複合索引的全部列都用上,並且查詢結果少的話,這樣就會造成「索引覆蓋」,於是性能能夠達到最優。同時,請記住:不管您是否常用聚合索引的其餘列,但其前導列必定要是使用最頻繁的列。

(四)其餘書上沒有的索引使用經驗總結

一、用聚合索引比用不是聚合索引的主鍵速度快

下面是實例語句:(都是提取25萬條數據)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

使用時間:3326毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000

使用時間:4470毫秒

這裏,用聚合索引比用不是聚合索引的主鍵速度快了近1/4。

二、用聚合索引比用通常的主鍵做order by時速度快,特別是在小數據量狀況下

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi

用時:12936

select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid

用時:18843

這裏,用聚合索引比用通常的主鍵做order by時,速度快了3/10。事實上,若是數據量很小的話,用匯集索引做爲排序列要比使用非彙集索引速度快得明顯的多;而數據量若是很大的話,如10萬以上,則兩者的速度差異不明顯。

三、使用聚合索引內的時間段,搜索時間會按數據佔整個數據表的百分比成比例減小,而不管聚合索引使用了多少個

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1'

用時:6343毫秒(提取100萬條)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-6-6'

用時:3170毫秒(提取50萬條)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

用時:3326毫秒(和上句的結果如出一轍。若是採集的數量同樣,那麼用大於號和等於號是同樣的)

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' and fariqi<'2004-6-6'

用時:3280毫秒

  4 、日期列不會由於有分秒的輸入而減慢查詢速度

下面的例子中,共有100萬條數據,2004年1月1日之後的數據有50萬條,但只有兩個不一樣的日期,日期精確到日;以前有數據50萬條,有5000個不一樣的日期,日期精確到秒。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' order by fariqi

用時:6390毫秒

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi<'2004-1-1' order by fariqi

用時:6453毫秒

(五)其餘注意事項

「水可載舟,亦可覆舟」,索引也同樣。索引有助於提升檢索性能,但過多或不當的索引也會致使系統低效。由於用戶在表中每加進一個索引,數據庫就要作更多的工做。過多的索引甚至會致使索引碎片。

因此說,咱們要創建一個「適當」的索引體系,特別是對聚合索引的建立,更應精益求精,以使您的數據庫能獲得高性能的發揮。

固然,在實踐中,做爲一個盡職的數據庫管理員,您還要多測試一些方案,找出哪一種方案效率最高、最爲有效。

2、改善SQL語句

不少人不知道SQL語句在SQL SERVER中是如何執行的,他們擔憂本身所寫的SQL語句會被SQL SERVER誤解。好比:

select * from table1 where name='zhangsan' and tID > 10000

和執行:

select * from table1 where tID > 10000 and name='zhangsan'

一些人不知道以上兩條語句的執行效率是否同樣,由於若是簡單的從語句前後上看,這兩個語句的確是不同,若是tID是一個聚合索引,那麼後一句僅僅從表的10000條之後的記錄中查找就好了;而前一句則要先從全表中查找看有幾個name='zhangsan'的,然後再根據限制條件條件tID>10000來提出查詢結果。

事實上,這樣的擔憂是沒必要要的。SQL SERVER中有一個「查詢分析優化器」,它能夠計算出where子句中的搜索條件並肯定哪一個索引能縮小表掃描的搜索空間,也就是說,它能實現自動優化。

雖然查詢優化器能夠根據where子句自動的進行查詢優化,但你們仍然有必要了解一下「查詢優化器」的工做原理,如非這樣,有時查詢優化器就會不按照您的本意進行快速查詢。

在查詢分析階段,查詢優化器查看查詢的每一個階段並決定限制須要掃描的數據量是否有用。若是一個階段能夠被用做一個掃描參數(SARG),那麼就稱之爲可優化的,而且能夠利用索引快速得到所需數據。

SARG的定義:用於限制搜索的一個操做,由於它一般是指一個特定的匹配,一個值得範圍內的匹配或者兩個以上條件的AND鏈接。形式以下:

列名操做符 <常數或變量>

<常數或變量> 操做符列名

列名能夠出如今操做符的一邊,而常數或變量出如今操做符的另外一邊。如:

Name=’張三’

價格>5000

5000<價格

Name=’張三’ and 價格>5000

若是一個表達式不能知足SARG的形式,那它就沒法限制搜索的範圍了,也就是SQL SERVER必須對每一行都判斷它是否知足WHERE子句中的全部條件。因此一個索引對於不知足SARG形式的表達式來講是無用的。

介紹完SARG後,咱們來總結一下使用SARG以及在實踐中遇到的和某些資料上結論不一樣的經驗:

一、Like語句是否屬於SARG取決於所使用的通配符的類型

如:name like ‘張%’ ,這就屬於SARG

而:name like ‘%張’ ,就不屬於SARG。

緣由是通配符%在字符串的開通使得索引沒法使用。

二、or 會引發全表掃描

Name=’張三’ and 價格>5000 符號SARG,而:Name=’張三’ or 價格>5000 則不符合SARG。使用or會引發全表掃描。

三、非操做符、函數引發的不知足SARG形式的語句

不知足SARG形式的語句最典型的狀況就是包括非操做符的語句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外還有函數。下面就是幾個不知足SARG形式的例子:

ABS(價格)<5000

Name like ‘%三’

有些表達式,如:

WHERE 價格*2>5000

SQL SERVER也會認爲是SARG,SQL SERVER會將此式轉化爲:

WHERE 價格>2500/2

但咱們不推薦這樣使用,由於有時SQL SERVER不能保證這種轉化與原始表達式是徹底等價的。

四、IN 的做用至關與OR

語句:

Select * from table1 where tid in (2,3)

Select * from table1 where tid=2 or tid=3

是同樣的,都會引發全表掃描,若是tid上有索引,其索引也會失效。

五、儘可能少用NOT

六、exists 和 in 的執行效率是同樣的

不少資料上都顯示說,exists要比in的執行效率要高,同時應儘量的用not exists來代替not in。但事實上,我試驗了一下,發現兩者不管是前面帶不帶not,兩者之間的執行效率都是同樣的。由於涉及子查詢,咱們試驗此次用SQL SERVER自帶的pubs數據庫。運行前咱們能夠把SQL SERVER的statistics I/O狀態打開。

(1)select title,price from titles where title_id in (select title_id from sales where qty>30)

該句的執行結果爲:

表 'sales'。掃描計數 18,邏輯讀 56 次,物理讀 0 次,預讀 0 次。

表 'titles'。掃描計數 1,邏輯讀 2 次,物理讀 0 次,預讀 0 次。

(2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)

第二句的執行結果爲:

表 'sales'。掃描計數 18,邏輯讀 56 次,物理讀 0 次,預讀 0 次。

表 'titles'。掃描計數 1,邏輯讀 2 次,物理讀 0 次,預讀 0 次。

咱們今後能夠看到用exists和用in的執行效率是同樣的。

七、用函數charindex()和前面加通配符%的LIKE執行效率同樣

前面,咱們談到,若是在LIKE前面加上通配符%,那麼將會引發全表掃描,因此其執行效率是低下的。但有的資料介紹說,用函數charindex()來代替LIKE速度會有大的提高,經我試驗,發現這種說明也是錯誤的:

select gid,title,fariqi,reader from tgongwen where charindex('刑偵支隊',reader)>0 and fariqi>'2004-5-5'

用時:7秒,另外:掃描計數 4,邏輯讀 7155 次,物理讀 0 次,預讀 0 次。

select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑偵支隊' + '%' and fariqi>'2004-5-5'

用時:7秒,另外:掃描計數 4,邏輯讀 7155 次,物理讀 0 次,預讀 0 次。

八、union並不絕對比or的執行效率高

咱們前面已經談到了在where子句中使用or會引發全表掃描,通常的,我所見過的資料都是推薦這裏用union來代替or。事實證實,這種說法對於大部分都是適用的。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or gid>9990000

用時:68秒。掃描計數 1,邏輯讀 404008 次,物理讀 283 次,預讀 392163 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000

用時:9秒。掃描計數 8,邏輯讀 67489 次,物理讀 216 次,預讀 7499 次。

看來,用union在一般狀況下比用or的效率要高的多。

但通過試驗,筆者發現若是or兩邊的查詢列是同樣的話,那麼用union則反倒和用or的執行速度差不少,雖然這裏union掃描的是索引,而or掃描的是全表。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or fariqi='2004-2-5'

用時:6423毫秒。掃描計數 2,邏輯讀 14726 次,物理讀 1 次,預讀 7176 次。

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'

union

select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-2-5'

用時:11640毫秒。掃描計數 8,邏輯讀 14806 次,物理讀 108 次,預讀 1144 次。

九、字段提取要按照「需多少、提多少」的原則,避免「select *」

咱們來作一個試驗:

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用時:4673毫秒

select top 10000 gid,fariqi,title from tgongwen order by gid desc

用時:1376毫秒

select top 10000 gid,fariqi from tgongwen order by gid desc

用時:80毫秒

由此看來,咱們每少提取一個字段,數據的提取速度就會有相應的提高。提高的速度還要看您捨棄的字段的大小來判斷。

十、count(*)不比count(字段)慢

某些資料上說:用*會統計全部列,顯然要比一個世界的列名效率低。這種說法實際上是沒有根據的。咱們來看:

select count(*) from Tgongwen

用時:1500毫秒

select count(gid) from Tgongwen

用時:1483毫秒

select count(fariqi) from Tgongwen

用時:3140毫秒

select count(title) from Tgongwen

用時:52050毫秒

從以上能夠看出,若是用count(*)和用count(主鍵)的速度是至關的,而count(*)卻比其餘任何除主鍵之外的字段彙總速度要快,並且字段越長,彙總的速度就越慢。我想,若是用count(*), SQL SERVER可能會自動查找最小字段來彙總的。固然,若是您直接寫count(主鍵)將會來的更直接些。

十一、order by按彙集索引列排序效率最高

咱們來看:(gid是主鍵,fariqi是聚合索引列)

select top 10000 gid,fariqi,reader,title from tgongwen

用時:196 毫秒。掃描計數 1,邏輯讀 289 次,物理讀 1 次,預讀 1527 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc

用時:4720毫秒。掃描計數 1,邏輯讀 41956 次,物理讀 0 次,預讀 1287 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

用時:4736毫秒。掃描計數 1,邏輯讀 55350 次,物理讀 10 次,預讀 775 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc

用時:173毫秒。掃描計數 1,邏輯讀 290 次,物理讀 0 次,預讀 0 次。

select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc

用時:156毫秒。掃描計數 1,邏輯讀 289 次,物理讀 0 次,預讀 0 次。

從以上咱們能夠看出,不排序的速度以及邏輯讀次數都是和「order by 彙集索引列」 的速度是至關的,但這些都比「order by 非彙集索引列」的查詢速度是快得多的。

同時,按照某個字段進行排序的時候,不管是正序仍是倒序,速度是基本至關的。

十二、高效的TOP

事實上,在查詢和提取超大容量的數據集時,影響數據庫響應時間的最大因素不是數據查找,而是物理的I/0操做。如:

select top 10 * from (

select top 10000 gid,fariqi,title from tgongwen

where neibuyonghu='辦公室'

order by gid desc) as a

order by gid asc

這條語句,從理論上講,整條語句的執行時間應該比子句的執行時間長,但事實相反。由於,子句執行後返回的是10000條記錄,而整條語句僅返回10條語句,因此影響數據庫響應時間最大的因素是物理I/O操做。而限制物理I/O操做此處的最有效方法之一就是使用TOP關鍵詞了。TOP關鍵詞是SQL SERVER中通過系統優化過的一個用來提取前幾條或前幾個百分比數據的詞。經筆者在實踐中的應用,發現TOP確實很好用,效率也很高。但這個詞在另一個大型數據庫ORACLE中卻沒有,這不能說不是一個遺憾,雖然在ORACLE中能夠用其餘方法(如:rownumber)來解決。在之後的關於「實現千萬級數據的分頁顯示存儲過程」的討論中,咱們就將用到TOP這個關鍵詞。

到此爲止,咱們上面討論瞭如何實現從大容量的數據庫中快速地查詢出您所須要的數據方法。固然,咱們介紹的這些方法都是「軟」方法,在實踐中,咱們還要考慮各類「硬」因素,如:網絡性能、服務器的性能、操做系統的性能,甚至網卡、交換機等。

  3、實現小數據量和海量數據的通用分頁顯示存儲過程

創建一個web 應用,分頁瀏覽功能必不可少。這個問題是數據庫處理中十分常見的問題。經典的數據分頁方法是:ADO 紀錄集分頁法,也就是利用ADO自帶的分頁功能(利用遊標)來實現分頁。但這種分頁方法僅適用於較小數據量的情形,由於遊標自己有缺點:遊標是存放在內存中,很費內存。遊標一創建,就將相關的記錄鎖住,直到取消遊標。遊標提供了對特定集合中逐行掃描的手段,通常使用遊標來逐行遍歷數據,根據取出數據條件的不一樣進行不一樣的操做。而對於多表和大表中定義的遊標(大的數據集合)循環很容易使程序進入一個漫長的等待甚至死機。

更重要的是,對於很是大的數據模型而言,分頁檢索時,若是按照傳統的每次都加載整個數據源的方法是很是浪費資源的。如今流行的分頁方法通常是檢索頁面大小的塊區的數據,而非檢索全部的數據,而後單步執行當前行。

最先較好地實現這種根據頁面大小和頁碼來提取數據的方法大概就是「俄羅斯存儲過程」。這個存儲過程用了遊標,因爲遊標的侷限性,因此這個方法並無獲得你們的廣泛承認。

後來,網上有人改造了此存儲過程,下面的存儲過程就是結合咱們的辦公自動化實例寫的分頁存儲過程:

CREATE procedure pagination1

(@pagesize int, --頁面大小,如每頁存儲20條記錄

@pageindex int  --當前頁碼

)

as

set nocount on

begin

declare @indextable table(id int identity(1,1),nid int) --定義表變量

declare @PageLowerBound int --定義此頁的底碼

declare @PageUpperBound int --定義此頁的頂碼

set @PageLowerBound=(@pageindex-1)*@pagesize

set @PageUpperBound=@PageLowerBound+@pagesize

set rowcount @PageUpperBound

insert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi desc

select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nid

and t.id>@PageLowerBound and t.id<=@PageUpperBound order by t.id

end

set nocount off

以上存儲過程運用了SQL SERVER的最新技術――表變量。應該說這個存儲過程也是一個很是優秀的分頁存儲過程。固然,在這個過程當中,您也能夠把其中的表變量寫成臨時表:CREATE TABLE #Temp。但很明顯,在SQL SERVER中,用臨時表是沒有用表變量快的。因此筆者剛開始使用這個存儲過程時,感受很是的不錯,速度也比原來的ADO的好。但後來,我又發現了比此方法更好的方法。

筆者曾在網上看到了一篇小短文《從數據表中取出第n條到第m條的記錄的方法》,全文以下:

從publish 表中取出第 n 條到第 m 條的記錄:

SELECT TOP m-n+1 *

FROM publish

WHERE (id NOT IN

(SELECT TOP n-1 id

FROM publish))

id 爲publish 表的關鍵字

我當時看到這篇文章的時候,真的是精神爲之一振,以爲思路很是得好。等到後來,我在做辦公自動化系統(ASP.net+ C#+SQL SERVER)的時候,突然想起了這篇文章,我想若是把這個語句改造一下,這就多是一個很是好的分頁存儲過程。因而我就滿網上找這篇文章,沒想到,文章還沒找到,卻找到了一篇根據此語句寫的一個分頁存儲過程,這個存儲過程也是目前較爲流行的一種分頁存儲過程,我很後悔沒有爭先把這段文字改形成存儲過程:

CREATE PROCEDURE pagination2

(

@SQL nVARCHAR(4000),  --不帶排序語句的SQL語句

@Page int,       --頁碼

@RecsPerPage int,    --每頁容納的記錄數

@ID VARCHAR(255),    --須要排序的不重複的ID號

@Sort VARCHAR(255)   --排序字段及規則

)

AS

DECLARE @Str nVARCHAR(4000)

SET @Str='SELECT  TOP '+CAST(@RecsPerPage AS VARCHAR(20))+' * FROM ('+@SQL+') T WHERE T.'+@ID+'NOT IN

(SELECT  TOP '+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+' '+@ID+' FROM ('+@SQL+') T9 ORDER BY '+@Sort+') ORDER BY '+@Sort

PRINT @Str

EXEC sp_ExecuteSql @Str

GO

其實,以上語句能夠簡化爲:

SELECT TOP 頁大小 *

FROM Table1

WHERE (ID NOT IN

(SELECT TOP 頁大小*頁數 id

FROM 表

ORDER BY id))

ORDER BY ID

但這個存儲過程有一個致命的缺點,就是它含有NOT IN字樣。雖然我能夠把它改造爲:

SELECT TOP 頁大小 *

FROM Table1

WHERE not exists

(select * from (select top (頁大小*頁數) * from table1 order by id) b where b.id=a.id )

order by id

即,用not exists來代替not in,但咱們前面已經談過了,兩者的執行效率其實是沒有區別的。

既便如此,用TOP 結合NOT IN的這個方法仍是比用遊標要來得快一些。

雖然用not exists並不能挽救上個存儲過程的效率,但使用SQL SERVER中的TOP關鍵字倒是一個很是明智的選擇。由於分頁優化的最終目的就是避免產生過大的記錄集,而咱們在前面也已經提到了TOP的優點,經過TOP 便可實現對數據量的控制。

在分頁算法中,影響咱們查詢速度的關鍵因素有兩點:TOP和NOT IN。TOP能夠提升咱們的查詢速度,而NOT IN會減慢咱們的查詢速度,因此要提升咱們整個分頁算法的速度,就要完全改造NOT IN,同其餘方法來替代它。

咱們知道,幾乎任何字段,咱們均可以經過max(字段)或min(字段)來提取某個字段中的最大或最小值,因此若是這個字段不重複,那麼就能夠利用這些不重複的字段的max或min做爲分水嶺,使其成爲分頁算法中分開每頁的參照物。在這裏,咱們能夠用操做符「>」或「<」號來完成這個使命,使查詢語句符合SARG形式。如:

Select top 10 * from table1 where id>200

因而就有了以下分頁方案:

select top 頁大小 *

from table1

where id>

(select max (id) from

(select top ((頁碼-1)*頁大小) id from table1 order by id) as T

)

order by id

在選擇即不重複值,又容易分辨大小的列時,咱們一般會選擇主鍵。下表列出了筆者用有着1000萬數據的辦公自動化系統中的表,在以GID(GID是主鍵,但並非彙集索引。)爲排序列、提取gid,fariqi,title字段,分別以第一、十、100、500、1000、1萬、10萬、25萬、50萬頁爲例,測試以上三種分頁方案的執行速度:(單位:毫秒)

頁 碼

方案1

方案2

方案3

1

60

30

76

10

46

16

63

100

1076

720

130

500

540

12943

83

1000

17110

470

250

1萬

24796

4500

140

10萬

38326

42283

1553

25萬

28140

128720

2330

50萬

121686

127846

7168

從上表中,咱們能夠看出,三種存儲過程在執行100頁如下的分頁命令時,都是能夠信任的,速度都很好。但第一種方案在執行分頁1000頁以上後,速度就降了下來。第二種方案大約是在執行分頁1萬頁以上後速度開始降了下來。而第三種方案卻始終沒有大的降勢,後勁仍然很足。

在肯定了第三種分頁方案後,咱們能夠據此寫一個存儲過程。你們知道SQL SERVER的存儲過程是事先編譯好的SQL語句,它的執行效率要比經過WEB頁面傳來的SQL語句的執行效率要高。下面的存儲過程不只含有分頁方案,還會根據頁面傳來的參數來肯定是否進行數據總數統計。

-- 獲取指定頁的數據

CREATE PROCEDURE pagination3

@tblName  varchar(255),    -- 表名

@strGetFields varchar(1000) = '*', -- 須要返回的列

@fldName varchar(255)='',   -- 排序的字段名

@PageSize  int = 10,     -- 頁尺寸

@PageIndex int = 1,      -- 頁碼

@doCount bit = 0,  -- 返回記錄總數, 非 0 值則返回

@OrderType bit = 0, -- 設置排序類型, 非 0 值則降序

@strWhere varchar(1500) = '' -- 查詢條件 (注意: 不要加 where)

AS

declare @strSQL  varchar(5000)    -- 主語句

declare @strTmp  varchar(110)    -- 臨時變量

declare @strOrder varchar(400)    -- 排序類型

if @doCount != 0

begin

if @strWhere !=''

set @strSQL = "select count(*) as Total from [" + @tblName + "] where "+@strWhere

else

set @strSQL = "select count(*) as Total from [" + @tblName + "]"

end

--以上代碼的意思是若是@doCount傳遞過來的不是0,就執行總數統計。如下的全部代碼都是@doCount爲0的狀況

else

begin

if @OrderType != 0

begin

set @strTmp = "<(select min"

set @strOrder = " order by [" + @fldName +"] desc"

--若是@OrderType不是0,就執行降序,這句很重要!

end

else

begin

set @strTmp = ">(select max"

set @strOrder = " order by [" + @fldName +"] asc"

end

if @PageIndex = 1

begin

if @strWhere != ''

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from [" + @tblName + "] where " + @strWhere + " " + @strOrder

else

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["+ @tblName + "] "+ @strOrder

--若是是第一頁就執行以上代碼,這樣會加快執行速度

end

else

begin

--如下代碼賦予了@strSQL以真正執行的SQL代碼

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["

+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "] from [" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder

if @strWhere != ''

set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["

+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["

+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["

+ @fldName + "] from [" + @tblName + "] where " + @strWhere + " "

+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder

end

end

exec (@strSQL)

GO

上面的這個存儲過程是一個通用的存儲過程,其註釋已寫在其中了。

在大數據量的狀況下,特別是在查詢最後幾頁的時候,查詢時間通常不會超過9秒;而用其餘存儲過程,在實踐中就會致使超時,因此這個存儲過程很是適用於大容量數據庫的查詢。

筆者但願可以經過對以上存儲過程的解析,能給你們帶來必定的啓示,並給工做帶來必定的效率提高,同時但願同行提出更優秀的實時數據分頁算法。

4、彙集索引的重要性和如何選擇彙集索引

在上一節的標題中,筆者寫的是:實現小數據量和海量數據的通用分頁顯示存儲過程。這是由於在將本存儲過程應用於「辦公自動化」系統的實踐中時,筆者發現這第三種存儲過程在小數據量的狀況下,有以下現象:

一、分頁速度通常維持在1秒和3秒之間。

二、在查詢最後一頁時,速度通常爲5秒至8秒,哪怕分頁總數只有3頁或30萬頁。

雖然在超大容量狀況下,這個分頁的實現過程是很快的,但在分前幾頁時,這個1-3秒的速度比起第一種甚至沒有通過優化的分頁方法速度還要慢,借用戶的話說就是「尚未ACCESS數據庫速度快」,這個認識足以致使用戶放棄使用您開發的系統。

筆者就此分析了一下,原來產生這種現象的癥結是如此的簡單,但又如此的重要:排序的字段不是彙集索引!

本篇文章的題目是:「查詢優化及分頁算法方案」。筆者只因此把「查詢優化」和「分頁算法」這兩個聯繫不是很大的論題放在一塊兒,就是由於兩者都須要一個很是重要的東西――彙集索引。

在前面的討論中咱們已經提到了,彙集索引有兩個最大的優點:

一、以最快的速度縮小查詢範圍。

二、以最快的速度進行字段排序。

第1條多用在查詢優化時,而第2條多用在進行分頁時的數據排序。

而彙集索引在每一個表內又只能創建一個,這使得彙集索引顯得更加的重要。彙集索引的挑選能夠說是實現「查詢優化」和「高效分頁」的最關鍵因素。

但要既使彙集索引列既符合查詢列的須要,又符合排序列的須要,這一般是一個矛盾。

筆者前面「索引」的討論中,將fariqi,即用戶發文日期做爲了彙集索引的起始列,日期的精確度爲「日」。這種做法的優勢,前面已經提到了,在進行劃時間段的快速查詢中,比用ID主鍵列有很大的優點。

但在分頁時,因爲這個彙集索引列存在着重複記錄,因此沒法使用max或min來最爲分頁的參照物,進而沒法實現更爲高效的排序。而若是將ID主鍵列做爲彙集索引,那麼彙集索引除了用以排序以外,沒有任何用處,其實是浪費了彙集索引這個寶貴的資源。

爲解決這個矛盾,筆者後來又添加了一個日期列,其默認值爲getdate()。用戶在寫入記錄時,這個列自動寫入當時的時間,時間精確到毫秒。即便這樣,爲了不可能性很小的重合,還要在此列上建立UNIQUE約束。將此日期列做爲彙集索引列。

有了這個時間型彙集索引列以後,用戶就既能夠用這個列查找用戶在插入數據時的某個時間段的查詢,又能夠做爲惟一列來實現max或min,成爲分頁算法的參照物。

通過這樣的優化,筆者發現,不管是大數據量的狀況下仍是小數據量的狀況下,分頁速度通常都是幾十毫秒,甚至0毫秒。而用日期段縮小範圍的查詢速度比原來也沒有任何遲鈍。

彙集索引是如此的重要和珍貴,因此筆者總結了一下,必定要將彙集索引創建在:

一、您最頻繁使用的、用以縮小查詢範圍的字段上;

二、您最頻繁使用的、須要排序的字段上。

應用程序設計

應用程序設計在決定使用 Microsoft? SQL Server? 2000 的系統的性能方面起關鍵做用。將客戶端視爲控制實體而非數據庫服務器。客戶端肯定查詢類型、什麼時候提交查詢以及如何處理查詢結果。這反過來對服務器上的鎖類型和持續時間、I/O 活動量以及處理(CPU) 負荷等產生主要影響,並由此影響整體性能的優劣。

正由於如此,在應用程序的設計階段作出正確決策十分重要。然而,即便在使用總控應用程序時(這種狀況下彷佛不可能更改客戶端應用程序)出現性能問題,也不會改變影響性能的根本因素:客戶端具備支配做用,若是不更改客戶端則許多性能問題都沒法解決。設計優秀的應用程序容許 SQL Server 支持成千上萬的併發用戶。反之,設計差的應用程序會防礙即便是最強大的服務器平臺處理少數用戶的請求。

客戶端應用程序的設計準則包括:

·                     消除過多的網絡流量。

客戶端和 SQL Server 之間的網絡往返一般是數據庫應用程序性能較差的首要緣由,甚至超過了服務器和客戶端之間傳送的數據量這一因素的影響。網絡往返描述在客戶端應用程序和 SQL Server 之間爲每一個批處理和結果集發送的會話流量。經過使用存儲過程,能夠將網絡往返減到最小。例如,若是應用程序根據從 SQL Server 收到的數據值採起不一樣的操做,只要可能就應直接在存儲過程當中作出決定,從而消除過多的網絡流量。

若是存儲過程當中有多個語句,則默認狀況下,SQL Server 在每一個語句完成時給客戶端應用程序發送一條消息,詳細說明每一個語句所影響的行數。大多數應用程序不須要這些消息。若是確信應用程序不須要它們,能夠禁用這些消息,以提升慢速網絡的性能。請使用 SET NOCOUNT 會話設置爲應用程序禁用這些消息。有關更多信息,請參見 SET NOCOUNT。

·                     使用小結果集。

檢索不必大的結果集(如包含上千行)並在客戶端瀏覽將增長 CPU 和網絡 I/O 的負載,使應用程序的遠程使用能力下降並限制多用戶可伸縮性。最好將應用程序設計爲提示用戶輸入足夠的信息,以便查詢提交後生成大小適中的結果集。有關更多信息,請參見使用高效數據檢索優化應用程序性能。

可幫助實現上述目標的應用程序設計技術包括:在生成查詢時對通配符進行控制,強制某些輸入字段,不容許特殊查詢,以及使用 TOP、PERCENT 或 SET ROWCOUNT 等 Transact-SQL 語句限制查詢返回的行數。有關更多信息,請參見使用 TOP 和PERCENT 限制結果集和 SET ROWCOUNT。

·                     容許在用戶須要從新控制應用程序時取消正在執行的查詢。

應用程序決不該強迫用戶從新啓動客戶機以取消查詢。無視這一點將致使沒法解決的性能問題。若是應用程序取消查詢(例如使用開放式數據庫鏈接 (ODBC) sqlcancel 函數取消查詢),應對事務級別予以適當的考慮。例如,取消查詢並不會提交或回滾用戶定義的事務。取消查詢後,全部在事務內獲取的鎖都將保留。所以,在取消查詢後始終要提交或回滾事務。一樣的狀況也適用於可用於取消查詢的 DB-Library 和其它應用程序接口 (API)。

·                     始終實現查詢或鎖定超時。

不要讓查詢無限期運行。調用適當的 API 以設置查詢超時。例如,使用 ODBC SQLSetStmtOption 函數。

有關設置查詢超時的更多信息,請參見 ODBC API 文檔。

有關設置鎖定超時的更多信息,請參見自定義鎖超時。

·                     不要使用不容許顯式控制發送到 SQL Server 的 SQL 語句的應用程序開發工具。

若是工具基於更高級的對象透明地生成 Transact-SQL 語句,並且不提供諸如查詢取消、查詢超時和徹底事務控制等關鍵功能,則不要使用這類工具。若是應用程序生成透明的 SQL 語句,一般不可能維護好的性能或解決性能問題,由於在這種狀況下不容許對事務和鎖定問題進行顯式控制,而這一點對性能情況相當重要。

·                     不要將決策支持和聯機事務處理 (OLTP) 查詢混在一塊兒。有關更多信息,請參見聯機事務處理與決策支持。

·                     只在必要時才使用遊標。

遊標是關係數據庫中的有用工具,但使用遊標完成任務始終比使用面向集合的 SQL 語句花費多。

當使用面向集合的 SQL 語句時,客戶端應用程序讓服務器更新知足指定條件的記錄集。服務器決定如何做爲單個工做單元完成更新。當經過遊標更新時,客戶端應用程序要求服務器爲每行維護行鎖或版本信息,而這只是爲了客戶端在提取行後請求更新行。

並且,使用遊標意味着服務器一般要在臨時存儲中維護客戶端的狀態信息,如用戶在服務器上的當前行集。爲衆多客戶端維護這類狀態信息需消耗大量的服務器資源。對於關係數據庫,更好的策略是讓客戶端應用程序快速進出,以便在各次調用之間不在服務器上維護客戶端的狀態信息。面向集合的 SQL 語句支持此策略。

然而,若是查詢使用遊標,請肯定若是使用更高效的遊標類型(如快速只進遊標)或單個查詢可否更高效地編寫遊標查詢。有關更多信息,請參見使用高效數據檢索優化應用程序性能。

·                     使事務儘量簡短。有關更多信息,請參見事務和批處理對應用程序性能的影響。

·                     使用存儲過程。有關更多信息,請參見存儲過程對應用程序性能的影響。

·                     使用 Prepared Execution 來執行參數化 SQL 語句。有關更多信息,請參見 Prepared Execution (ODBC)。

·                     始終處理完全部結果。

不要設計或使用在未取消查詢時就中止處理結果行的應用程序。不然一般會致使阻塞和下降性能。有關更多信息,請參見瞭解和避免阻塞。

·                     確保將應用程序設計爲可避免死鎖。有關更多信息,請參見將死鎖減至最少。

·                     確保已設置全部可以優化分佈式查詢性能的適當選項。有關更多信息,請參見優化分佈式查詢。

在應用系統的設計中,要着重考慮如下幾點:

  1.合理使用索引

索引是數據庫中重要的數據結構,它的根本目的就是爲了提升查詢效率。如今大多數的數據庫產品都採用IBM最早提出的ISAM索引結構。索引的使用要恰到好處,其使用原則以下:

●在常常進行鏈接,可是沒有指定爲外鍵的列上創建索引,而不常常鏈接的字段則由優化器自動生成索引。

●在頻繁進行排序或分組(即進行group by或order by操做)的列上創建索引。

●在條件表達式中常常用到的不一樣值較多的列上創建檢索,在不一樣值少的列上不要創建索引。好比在僱員表的「性別」列上只有「男」與「女」兩個不一樣值,所以就無必要創建索引。若是創建索引不但不會提升查詢效率,反而會嚴重下降更新速度。

●若是待排序的列有多個,能夠在這些列上創建複合索引(compound index)。

●使用系統工具。如Informix數據庫有一個tbcheck工具,能夠在可疑的索引上進行檢查。在一些數據庫服務器上,索引可能失效或者由於頻繁操做而使得讀取效率下降,若是一個使用索引的查詢不明不白地慢下來,能夠試着用tbcheck工具檢查索引的完整性,必要時進行修復。另外,當數據庫表更新大量數據後,刪除並重建索引能夠提升查詢速度。

2.避免或簡化排序

應當簡化或避免對大型表進行重複的排序。當可以利用索引自動以適當的次序產生輸出時,優化器就避免了排序的步驟。如下是一些影響因素:

●索引中不包括一個或幾個待排序的列;

●group by或order by子句中列的次序與索引的次序不同;

●排序的列來自不一樣的表。

爲了不沒必要要的排序,就要正確地增建索引,合理地合併數據庫表(儘管有時可能影響表的規範化,但相對於效率的提升是值得的)。若是排序不可避免,那麼應當試圖簡化它,如縮小排序的列的範圍等。

3.消除對大型錶行數據的順序存取

在嵌套查詢中,對錶的順序存取對查詢效率可能產生致命的影響。好比採用順序存取策略,一個嵌套3層的查詢,若是每層都查詢1000行,那麼這個查詢就要查詢10億行數據。避免這種狀況的主要方法就是對鏈接的列進行索引。例如,兩個表:學生表(學號、姓名、年齡……)和選課表(學號、課程號、成績)。若是兩個表要作鏈接,就要在「學號」這個鏈接字段上創建索引。

還能夠使用並集來避免順序存取。儘管在全部的檢查列上都有索引,但某些形式的where子句強迫優化器使用順序存取。下面的查詢將強迫對orders表執行順序操做:

SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008

雖然在customer_num和order_num上建有索引,可是在上面的語句中優化器仍是使用順序存取路徑掃描整個表。由於這個語句要檢索的是分離的行的集合,因此應該改成以下語句:

SELECT * FROM orders WHERE customer_num=104 AND order_num>1001

UNION

SELECT * FROM orders WHERE order_num=1008

這樣就能利用索引路徑處理查詢。

4.避免相關子查詢

一個列的標籤同時在主查詢和where子句中的查詢中出現,那麼極可能當主查詢中的列值改變以後,子查詢必須從新查詢一次。查詢嵌套層次越多,效率越低,所以應當儘可能避免子查詢。若是子查詢不可避免,那麼要在子查詢中過濾掉儘量多的行。

5.避免困難的正規表達式

MATCHES和LIKE關鍵字支持通配符匹配,技術上叫正規表達式。但這種匹配特別耗費時間。例如:SELECT * FROM customer WHERE zipcode LIKE 「98_ _ _」

即便在zipcode字段上創建了索引,在這種狀況下也仍是採用順序掃描的方式。若是把語句改成SELECT * FROM customer WHERE zipcode >「98000」,在執行查詢時就會利用索引來查詢,顯然會大大提升速度。

另外,還要避免非開始的子串。例如語句:SELECT * FROM customer WHERE zipcode[2,3] >「80」,在where子句中採用了非開始子串,於是這個語句也不會使用索引。

6.使用臨時表加速查詢

把表的一個子集進行排序並建立臨時表,有時能加速查詢。它有助於避免多重排序操做,並且在其餘方面還能簡化優化器的工做。例如:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

AND cust.postcode>「98000」

ORDER BY cust.name

若是這個查詢要被執行屢次而不止一次,能夠把全部未付款的客戶找出來放在一個臨時文件中,並按客戶的名字進行排序:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

優化實用工具和工具性能

可在生產數據庫上執行以得到最佳性能收益的三個操做包括:

·                     備份和還原操做。

·                     將數據大容量複製到表中。

·                     執行數據庫控制檯命令 (DBCC) 操做。

通常狀況下,不須要優化這些操做。然而,在性能很關鍵的情形中,可採用一些技巧優化性能。

 Microsoft SQL Server數據庫內核用1個基於費用的查詢優化器自動優化向SQL提交的數據查詢操做。數據操做查詢是指支持SQL關鍵字WHERE或HAVING的查詢,如SELECT、DELETE和UPDATE。基於費用的查詢優化器根據統計信息產生子句的費用估算。

  瞭解優化器數據處理過程的簡單方法是檢測SHOWPLAN命令的輸出結果。若是用基於字符的工具(例如isql),能夠經過鍵入SHOW SHOWPLAN ON來獲得SHOWPLAN命令的輸出。若是使用圖形化查詢,好比SQL Enterprise Manager中的查詢工具或isql/w,能夠設定配置選項來提供這一信息。

 SQL Server的優化經過3個階段完成:查詢分析、索引選擇、合併選擇:

1.查詢分析

  在查詢分析階段,SQL Server優化器查看每個由正規查詢樹表明的子句,並判斷它是否能被優化。SQL Server通常會盡可能優化那些限制掃描的子句。例如,搜索和/或合併子句。可是不是全部合法的SQL語法均可以分紅可優化的子句,如含有SQL不等關係符「<>」的子句。由於「<>」是1個排斥性的操做符,而不是1個包括性的操做符,所在掃描整個表以前沒法肯定子句的選擇範圍會有多大。當1個關係型查詢中含有不可優化的子句時,執行計劃用表掃描來訪問查詢的這個部分,對於查詢樹中可優化的SQL Server子句,則由優化器執行索引選擇。

2.索引選擇

  對於每一個可優化的子句,優化器都查看數據庫系統表,以肯定是否有相關的索引能用於訪問數據。只有當索引中的列的1個前綴與查詢子句中的列徹底匹配時,這個索引才被認爲是有用的。由於索引是根據列的順序構造的,因此要求匹配是精確的匹配。對於分簇索引,原來的數據也是根據索引列順序排序的。想用索引的次要列訪問數據,就像想在電話本中查找全部姓爲某個姓氏的條目同樣,排序基本上沒有什麼用,由於你仍是得查看每一行以肯定它是否符合條件。若是1個子句有可用的索引,那麼優化器就會爲它肯定選擇性。

  因此在設計過程當中,要根據查詢設計準則仔細檢查全部的查詢,以查詢的優化特色爲基礎設計索引。

  (1)比較窄的索引具備比較高的效率。對於比較窄的索引來講,每頁上能存放較多的索引行,並且索引的級別也較少。因此,緩存中能放置更多的索引頁,這樣也減小了I/O操做。

  (2)SQL Server優化器能分析大量的索引和合並可能性。因此與較少的寬索引相比,較多的窄索引能向優化器提供更多的選擇。可是不要保留沒必要要的索引,由於它們將增長存儲和維護的開支。對於複合索引、組合索引或多列索引,SQL Se

優化服務器性能

Microsoft? SQL Server? 2000 自動調整不少服務器配置選項,所以系統管理員只需作不多的調整(若是有)。這些配置選項能夠由系統管理員修改,但通常建議保留爲默認值,以使 SQL Server 能根據運行時的狀況自動對自身進行調整。

不過,若是須要,能夠配置下列組件以優化服務器性能:

·                     SQL Server 內存

·                     I/O 子系統

·                     Microsoft Windows NT? 選項

MSSQL是怎樣使用內存的:

  最大的開銷通常是用於數據緩存,若是內存足夠,它會把用過的數據和以爲你會用到的數據通通扔到內存中,直到內存不足的時候,才把命中率低的數據給清掉。因此通常咱們在看statistics io的時候,看到的physics read都是0。

  其次就是查詢的開銷,通常地說,hash join是會帶來比較大的內存開銷的,而merge join和nested loop的開銷比較小,還有排序和中間表、遊標也是會有比較大的開銷的。

  因此用於關聯和排序的列上通常須要有索引。

  再其次就是對執行計劃、系統數據的存儲,這些都是比較小的。

  咱們先來看數據緩存對性能的影響,若是系統中沒有其它應用程序來爭奪內存,數據緩

存通常是越多越好,甚至有些時候咱們會強行把一些數據pin在高速緩存中。可是若是有其?

τ貿絛潁淙輝諦枰氖焙騇SSQL會釋放內存,可是線程切換、IO等待這些工做也是須要

時間的,因此就會形成性能的下降。這樣咱們就必須設置MSSQL的最大內存使用。能夠在SQL

Server

屬性(內存選項卡)中找到配置最大使用內存的地方,或者也能夠使用sp_configure來完成

。若是沒有其它應用程序,那麼就不要限制MSSQL對內存的使用。

  而後來看查詢的開銷,這個開銷顯然是越低越好,由於咱們不能從中獲得好處,相反,

使用了越多的內存多半意味着查詢速度的下降。因此咱們通常要避免中間表和遊標的使用,

在常常做關聯和排序的列上創建索引。 不更改代碼的狀況下如何優化數據庫系統

這個問題不少DBA可能都碰到過吧:好比剛接手一箇舊有系統,原來的廠商不容許對代碼修?

模蛘呤竅低秤τ帽冉瞎丶2輝市磣饜薷模蛘呤竊創氤鮎諫桃的康模辛艘歡ǔ潭

鵲募用埽褂械氖焙蚩贍蓯切姓蛩?-

領導爲了不責任,不容許你這樣作,但這個時候,系統的性能上的問題還比較嚴重,還有

其餘辦法怎麼對系統進行優化麼? 在這裏我嘗試總結一下可能有的途徑。

針對特定的SQL進行"外科手術" (Metalink 122812.1),改進執行計劃 ·

更新統計信息 (調整採樣率/柱狀圖統計) ·                                 調整索引

(添加或調整合適的索引,刪除沒必要要的索引) ·

建立物化試圖(用空間開銷來換取時間收益) 優化OS和數據庫之外的其餘東西

首先優化操做系統-好比核心參數的合理調整,操做系統資源的合理分配;

磁盤IO的調整,這是很重要的一部分,由於磁盤IO速度很容易形成系統瓶頸;網絡資源的優化

-TCP/IP的參數調整; 調整Oracle初始化參數 優化器模式的設定,db_cache 參數等設定,sga

大小等參數設定,都對數據庫性能有着重要的影響。 合理的系統資源調度

在一些批處理操做爲主的系統中,系統資源的調度是比較重要的,調度不合理,很容易形成

資源爭用。有的系統可能在系統建立之初調度是比較合理的,通過一段時間運行以後,可能

由於數據量的變化,SQL語句的執行計劃變化等會形成操做時間上的重疊,這確定會給系統?

囪沽ι系奈侍狻?調整數據庫對象 ·                                 調整pctfree

,freelist ,存儲參數 ·

調整表空間文件和數據庫對象(表、索引)的磁盤分佈。 ·

cache 一些經常使用的數據庫對象。 系統Bug問題帶來的影響/升級改進性能

Oracle軟件Bug多多,系統運行初期有的Bug帶來的危害還不夠明顯,隨着時間的推移,個別

的Bug會給系統性能形成問題。這個時候對系統的Bug

修復已經對數據庫系統進行升級就是必要的。經過升級,修正Oracle軟件缺陷,同時在升級

後也可能會加強數據庫引擎的效率。固然,也要注意升級可能帶來的不良的影響。 ·

                         操做系統相關優化 1.

操做系統性能的好壞直接影響數據庫的使用性能,若是操做系統存在問題,如CPU過載、過?

饒詿娼換弧⒋排蘄/O瓶頸等,在這種狀況下,單純進行數據庫內部性能調整是不會改善系統

性能的。咱們能夠經過Windows NT的系統監視器(System

Monitor)來監控各類設備,發現性能瓶頸。  CPU

一種常見的性能問題就是缺少處理能力。系統的處理能力是由系統的CPU數量、類型和速度?

齠ǖ摹H綣低趁揮兇愎壞腃PU處理能力,它就不能足夠快地處理事務以知足須要。咱們可

以使用System

Monitor肯定CPU的使用率,若是以75%或更高的速率長時間運行,就可能碰到了CPU瓶頸問題

,這時應該升級CPU。可是升級前必須監視系統的其餘特性,若是是由於SQL語句效率很是低

,優化語句就有助於解決較低的CPU利用率。而當肯定須要更強的處理能力,能夠添加CPU或

者用更快的CPU 替換。  內存 SQL Server可以使用的內存量是SQL

Server性能最關鍵因素之一。而內存同I/O子系統的關係也是一個很是重要的因素。例如,?

贗/O操做頻繁的系統中,SQL

Server用來緩存數據的可用內存越多,必須執行的物理I/O也就越少。這是由於數據將從數?

蓴捍嬤卸寥《皇譴喲排潭寥 M詿媼康牟蛔慊嵋鵜饗緣拇排潭列雌烤保蛭低

郴捍婺芰Σ蛔慊嵋鷥嗟奈錮澩排蘄/O。  能夠利用System Monitor檢查SQL

Server的Buffer Cache Hit

Ratio計數器,若是命中率常常低於90%,就應該添加更多的內存。  I/O子系統由I/O子系

統發生的瓶頸問題是數據庫系統可能遇到的最多見的同硬件有關的問題。配置不好的I/O子?

低騁鸚閱芪侍獾難現爻潭冉齟斡詒嘈春懿畹腟QL語句。I/O子系統問題是這樣產生的,一?

齟排糖髂芄恢蔥械腎/O操做是有限的,通常一個普通的磁盤驅動器每秒只能處理85次I/

O操做,若是磁盤驅動器超載,到這些磁盤驅動器的I/O操做就要排隊,SQL的I/O延遲將很長

。這可能會使鎖持續的時間更長,或者使線程在等待資源的過程當中保持空閒狀態,其結果就

是整個系統的性能受到影響。

解決I/O子系統有關的問題也許是最容易的,多數狀況下,增長磁盤驅動器就能夠解決這個?

閱芪侍狻!?

 固然,影響性能的因素不少,而應用又各不相同,找出一個通用的優化方案是很困難的,

只能是在系統開發和維護的過程當中針對運行的具體狀況,不斷加以調整。 2 與SQL

Server相關的硬件系統   與SQL

Server有關的硬件設計包括系統處理器、內存、磁盤子系統和網絡,這4個部分基本上構成?

擻布教ǎ琖indows NT和SQL Server運行於其上。 2.1 系統處理器(CPU)

  根據本身的具體須要肯定CPU結構的過程就是估計在硬件平臺上佔用CPU的工做量的過程

。從以往的經驗看,CPU配置最少應是1個80586/100處理器。若是隻有2~3個用戶,這就足?

渙耍綣蛩闃С指嗟撓沒Ш凸丶τ茫萍霾捎肞entium Pro或PⅡ級CPU。

2.2 內存(RAM)   爲SQL

Server方案肯定合適的內存設置對於實現良好的性能是相當重要的。SQL

Server用內存作過程緩存、數據和索引項緩存、靜態服務器開支和設置開支。SQL

Server最多能利用2GB虛擬內存,這也是最大的設置值。還有一點必須考慮的是Windows

NT和它的全部相關的服務也要佔用內存。   Windows

NT爲每一個WIN32應用程序提供了4GB的虛擬地址空間。這個虛擬地址空間由Windows

NT虛擬內存管理器(VMM)映射到物理內存上,在某些硬件平臺上能夠達到4GB。SQL

Server應用程序只知道虛擬地址,因此不能直接訪問物理內存,這個訪問是由VMM控制的。W

indows NT容許產生超出可用的物理內存的虛擬地址空間,這樣當給SQL

Server分配的虛擬內存多於可用的物理內存時,會下降SQL Server的性能。

  這些地址空間是專門爲SQL

Server系統設置的,因此若是在同一硬件平臺上還有其它軟件(如文件和打印共享,應用程?

蚍竦?在運行,那麼應該考慮到它們也佔用一部份內存。通常來講硬件平臺至少要配置32

MB的內存,其中,Windows

NT至少要佔用16MB。1個簡單的法則是,給每個併發的用戶增長100KB的內存。例如,若是

有100個併發的用戶,則至少須要32MB+100用戶*100KB=42MB內存,實際的使用數量還須要根

據運行的實際狀況調整。能夠說,提升內存是提升系統性能的最經濟的途徑。

 2.3 磁盤子系統   設計1個好的磁盤I/O系統是實現良好的SQL

Server方案的一個很重要的方面。這裏討論的磁盤子系統至少有1個磁盤控制設備和1個或多

個硬盤單元,還有對磁盤設置和文件系統的考慮。智能型SCSI-

2磁盤控制器或磁盤組控制器是不錯的選擇,其特色以下:

  (1)控制器高速緩存。  (2)總線主板上有處理器,能夠減小對系統CPU的中斷。  (

3)異步讀寫支持。  (4)32位RAID支持。  (5)快速SCSI—2驅動。  (6)超前讀高速緩

存(至少1個磁道)。 3 檢索策略

  在精心選擇了硬件平臺,又實現了1個良好的數據庫方案,而且具有了用戶需求和應用?

矯嫺鬧逗螅衷謨Ω蒙杓撇檠退饕恕S?個方面對於在SQL

Server上取得良好的查詢和索引性能是十分重要的,第1是根據SQL

Server優化器方面的知識生成查詢和索引;第2是利用SQL

Server的性能特色,增強數據訪問操做。

相關文章
相關標籤/搜索