數據庫調優的方法有那些

 

[轉自] http://www.myfreelinux.com/?p=538

 

1.引言    
    數據庫調優可以使數據庫應用運行得更快,它需要綜合考慮各種複雜的因素。將數據均 勻分佈在磁盤上可以提高I/O 利用率,提高數據的讀寫性能;適當程度的非規範化可以改善 系統查詢性能;建立索引和編寫高效的SQL 語句能有效避免低性能操作;通過鎖的調優解 決併發控制方面的性能問題。     數據庫調優技術可以在不同的數據庫系統中使用,它不必糾纏於複雜的公式和規則,然 而它需要對程序的應用、數據庫管理系統、查詢處理、併發控制、操作系統以及硬件有廣泛 而深刻的理解。

 2.計算機硬件調優

2.1 數據庫對象的放置策略     利用數據庫分區技術,均勻地把數據分佈在系統的磁盤中,平衡I/O 訪問,避免I/O 瓶 頸:    
    (1)訪問分散到不同的磁盤,即使用戶數據儘可能跨越多個設備,多個I/O 運轉,避免 I/O 競爭,克服訪問瓶頸;分別放置隨機訪問和連續訪問數據。    
    (2)分離系統數據庫I/O 和應用數據庫I/O,把系統審計表和臨時庫表放在不忙的磁盤 上。    
    (3)把事務日誌放在單獨的磁盤上,減少磁盤I/O 開銷,這還有利於在障礙後恢復,提 高了系統的安全性。    
    (4)把頻繁訪問的“活性”表放在不同的磁盤上;把頻繁用的表、頻繁做Join的表分別 放在單獨的磁盤上,甚至把頻繁訪問的表的字段放在不同的磁盤上,把訪問分散到不同的磁 盤上,避免I/O 爭奪。
2.2 使用磁盤硬件優化數據庫
     RAID (獨立磁盤冗餘陣列)是由多個磁盤驅動器(一個陣列)組成的磁盤系統。通過將磁盤陣列當作一個磁盤來對待,基於硬件的RAID允許用戶管理多個磁盤。使用基於硬件的 RAID與基於操作系統的RAID相比較,基於硬件的RAID能夠提供更佳的性能。如果使用基於操作系統的RAID,那麼它將佔據其他系統需求的CPU週期;通過使用基於硬件的RAID, 用戶在不關閉系統的情況下能夠替換髮生故障的驅動器。    
SQL Server 一般使用RAID等級0、1  和5。   
    RAID 0 是傳統的磁盤鏡象,陣列中每一個磁盤都有一個或多個磁盤拷貝,它主要用來 提供最高級的可靠性,使RAID 0成倍增加了寫操作卻可以並行處理多個讀操作,從而提高 了讀操作的性能。
    RAID 1 是磁盤鏡像或磁盤雙工,能夠爲事務日誌保證冗餘性。
    RAID 5帶奇偶的磁盤條帶化,即將數據信息和校驗信息分散到陣列的所有磁盤中,它可以消除一個校驗盤的瓶頸和單點失效問題,RAID 5 也會增加寫操作,也可以並行處理一個讀操作,還 可以成倍地提高讀操作的性能。    
    相比之下,RAID 5 增加的寫操作比RAID 0 增加的要少許多。在實際應用中,用戶的讀操作要求遠遠多於寫操作請求,而磁盤執行寫操作的速度很快,以至於用戶幾乎感覺不到增加的時間,所以增加的寫操作負擔不會帶來什麼問題。在性能較好的服務器中一般都會選擇使用RAID 5 的磁盤陣列卡來實現,對於性能相對差一些的服務器也可利用純軟件的方式來實現RAID 5。

3.關係系統與應用程序調優
    3.1 應用程序優化     從數據庫設計者的角度來看,應用程序無非是實現對數據的增加、修改、刪除、查詢和體現數據的結構和關係。設計者在性能方面的考慮因素,總的出發點是:把數據庫當作奢侈 的資源看待,在確保功能的同時,儘可能少地動用數據庫資源。包括如下原則:    
(    1)不訪問或少訪問數據庫;    
    (2)簡化對數據庫的訪問;    
    (3)使訪問最優;    
    (4 )對前期及後續的開發、部署、調整提出要求,以協助實現性能目標。   
     另外,不要直接執行完整的SQL  語法,儘量通過存儲過程來調用SQL Server。客戶與服務器連接時,建立連接池,讓連接儘量得以重用,以避免時間與資源的損耗。非到不得已, 不要使用遊標結構,確實使用時,注意各種遊標的特性。

3.2  基本表設計優化
     在基於表驅動的信息管理系統中,基本表的設計規範是第三範式。第三範式的基本特徵 是非主鍵屬性只依賴於主鍵屬性。基於第三範式的數據庫表設計具有很多優點:一是能消除 冗餘數據、節省磁盤存儲空間;二是有良好的數據完整性限制(基於主外鍵的參照完整限制 和基於主鍵的實體完整性限制),這使得數據容易維護、移植和更新;三是數據的可逆性好, 在做連接查詢或者合併表時不遺漏、不重複;四是消除了冗餘數據(這裏主要指冗餘列), 使得查詢時每個數據頁存儲的數據行增多,這樣就有效地減少了邏輯I/O,同時也減少了物 理I/O;五是對大多數事務而言,運行性能好;六是物理設計的機動性較大,能滿足日益增 長的用戶需求。
    基於第三範式設計的庫表雖然有其優越性,然而在實際應用中有時不利於系統運行性能 的優化:例如需要部分數據時而要掃描整表,許多過程同時競爭同一數據,反覆用相同行計 算相同的結果,過程從多表獲取數據時引發大量的連接操作,當數據來源於多表時的連接操 作;這都消耗了磁盤I/O 和CPU 時間。特別需要提出的是,在遇到下述情形時,我們要對 基本表進行擴展設計優化:許多過程要頻繁訪問一個表、子集數據訪問、重複計算和冗餘數 據,有時用戶要求一些過程優先或低的響應時間,爲避免以上不利因素,我們通常根據訪問 的頻繁程度對相關表進行分割處理、存儲冗餘數據、存儲衍生列、合併相關表處理,這些都 是克服這些不利因素和優化系統運行的有效途徑。 
    (1)分割表     分割表可分爲水平分割表和垂直分割表兩種:水平分割是按照行將一個表分割爲多個 表,這可以提高每個表的查詢速度,但是由於造成了多表連接,所以應該在同時查詢或更新 不同分割表中的列的情況比較少的情況下使用。垂直分割是對於一個列很多的表,若某些列 的訪問頻率遠遠高於其它列,在不破壞第三範式的前提下將主鍵和這些列作爲一個表,將主 鍵和其它列作爲另外一個表。一種是當多個過程頻繁訪問表的不同列時,可將表垂直分成幾 個表,減少磁盤I/O。通過減少列的寬度,增加了每個數據頁的行數,一次I/O 就可以掃描 更多的行,從而提高了訪問每一個表的速度。垂直分割表可以達到最大化利用Cache 的目的。 分割表的缺點是要在插入或刪除數據時要考慮數據的完整性,用存儲過程維護。 
    (2)存儲衍生數據     對一些要做大量重複性計算的過程而言,若重複計算過程得到的結果相同,或計算牽扯 多行數據需額外的磁盤I/O 開銷,或計算複雜需要大量的C P U 時間,就考慮存儲計算結果:若在一行或多行進行重複性計算,就在表內增加列存儲結果,但若參與計算的列被更新時, 必須要用觸發器或存儲過程更新這個新列。總之,存儲冗餘數據有利於加快訪問速度,但違 反了第三範式,這會增加維護數據完整性的代價,必須用觸發器立即更新、或存儲過程更新, 以維護數據的完整性。

3.3 修改應用技術模式
     引入“中間表”的概念,在實際單據未進入核心業務流程前,採用“中間表”的技術思 路,就是在實際用戶操作過程中,實際操作的是一個臨時表,在進行數據某個階段審覈(進 入下一個環節)後,將臨時表的數據寫入正式表,並且刪除臨時表的數據,這樣始終保持用 戶操作表的固定的數據量而且控制增長,可以定期清除。     採用臨時表技術首先需將要操作的數據集插入到臨時表中,這會給系統帶來額外的開 銷。這裏假設臨時表中的數據集遠小於源數據表中的數據集,因此在進行數據連接操作或對 數據集進行頻繁讀操作時,系統的性能會提高几倍甚至幾十倍不等。    
    並非所有情況都適宜用臨時表技術。一般來說,下面兩種情況適宜採用臨時表技術進行 處理:     
    (1)對數據量較大的表進行連接操作,並且連接操作的結果是一個小結果集。 
    (2)對數據量較大的表進行頻繁訪問,訪問的範圍比較固定且比較集中。 
    合理使用臨時表技術,有助於提高應用系統對大數據表的實時處理的性能。
4.數據庫索引優化    
    索引是建立在表上的一種數據組織,它能提高訪問表中一條或多條記錄的特定查詢效 率。利用索引優化系統性能是顯而易見的,對所有常用於查詢中的Where  子句的列和所有 用於排序的列創建索引,可以避免整表掃描或訪問,在不改變表的物理結構的情況下,直接 訪問特定的數據列,這樣可以減少數據存取時間;利用索引可以優化或排除耗時的分類操作, 把數據分散到不同的頁面上,這樣就分散了插入的數據;主鍵自動建立了唯一索引,因此唯 一索引也能確保數據的唯一性(即實體完整性)。總之,索引可以加快查詢速度、減少I/O 操作、消除磁盤排序。     優化索引可以避免掃描整個表,減少因查詢造成的開銷。一般說來建立索引要注意以下 幾點:     
    (1)檢查被索引的列或組合索引的首列是否出現在PL/SQL 語句的WHERE 子句中, 這是“執行計劃”能用到相關索引的必要條件。比較一下列中唯一鍵的數量和表中記錄的行 數,就可以判斷該列的可選擇性。如果該列的“唯一鍵的數量/表中記錄行數”的比值越接近 於1,則該列的可選擇行越高。在可選擇性高的列上進行查詢,返回的數據就較少,比較適 合索引查詢。相反,比如性別列上只有兩個值,可選擇行就很小,不適合索引查詢。因此, 在查詢中經常作爲條件表達式且不同值較多的列上建立索引,不同值較少的列上不要建立索 引。     
    (2)索引的創建也是需要代價的,對於刪除、某些更新、插入操作,對於每個索引都 要進行相應的刪除、更新、插入操作。從而導致刪除、某些更新、插入操作的效率變低。因 此頻繁進行刪除、插入操作的表不要建立過多的索引。     
    (3)查詢經常用到的列上建立非聚簇索引,在頻繁進行範圍查詢、排序、分組的列上 建立聚簇索引。     
    (4 )對於不存在重複值的列,創建唯一索引優於創建非唯一索引。     
    (5)當數據庫表更新大數據後,刪除並重新建立索引來提高查詢速度。     
    (6)當對一個表的update 操作遠遠多於select 操作時,不應創建索引。     
    (7)如果索引列是函數的參數,則索引在查詢時用不上,該列也不適合索引。     
    (8)Hash   Join (HJ )由於須做HASH 運算,索引的存在對數據查詢速度幾乎沒有影 響。     
    (9)在主鍵上建立索引,尤其當經常用它作爲連接的時候;在經常用於連接而又未指 定爲外鍵的列上建立索引。    
     (10)經常同時存取多列,且每列都含有重複值,可以考慮建立複合索引來覆蓋一個或 一組查詢,並且把查詢引用最頻繁的列作爲前導列。                                     
    (11)盡使用較窄的索引,這樣數據頁每頁上能因存放較多的索引行而減少操作。     
    (12)並行查詢將不會用到索引。     
    (13)索引中存儲值不能爲全空。     
    (14)查詢中較少用到的列、數據量較大的列均不應建立索引。

5.SQL 語句優化    
    在完成了系統設計、索引設計等工作以後,就要考慮在使用過程中對語句的設計了。 影響數據庫應用程序性能的一個重要因素是SQL 語句,按其影響嚴重程度,依次可分爲: 無謂的SQL,拙劣的SQL,複雜的SQL。     無謂的SQL:它們對數據庫的訪問,並不存在技術、技能上的問題,但卻不是必要的, 超出了實際業務需求。其結果是浪費了寶貴的主機資源、佔用了網絡流量,降低了系統性能。     拙劣的SQL:它們對數據庫的訪問並不是多餘的,所體現的業務邏輯或結果是正確的, 但是“寫法”不夠好,導致數據庫處理起來不夠優化。     複雜的SQL:數據庫中多表(或視圖)關聯,條件複雜、冗長,計算複雜,使用冷僻 的SQL 技術等。     其中,無謂的SQL 和拙劣的SQL 屬於開發技能方面的問題;複雜的SQL 屬於設計技 能方面的問題,設計到數據庫的結構。     在使用結構化查詢語言來執行查詢時,推薦以下舉措:    
    (1)擇運算應儘可能先做,並在對同一個表進行多個選擇運算時,選擇影響較大的語 句放在前面;較弱的選擇條件寫在後面,這樣就可以先根據較嚴格的條件得出數據較小的 信息,再在這些信息中根據後面較弱的條件得到滿足條件的信息。    
    (2)應避免使用相關子查詢。把子查詢轉換成聯結來實現。對於主查詢的每一條記錄子 查詢都要執行一次,嵌套的層次越多效率越低。避免對子句使用數學運算符。即不要對數 據表的屬性列進行操作。SQL 概念上將位於WHERE  子句中的相關子查詢,處理成獲取參 數並且返回一個單獨的值或值的集合的函數。因爲子查詢要對應位於外層查詢的每一個元組 進行單獨的計算。從而導致大量的隨機磁盤I/O 操作。所以在實際應用中若可以用連接代替 的子查詢,則用連接實現。例如,有以下相關子查詢語句:    
SELECT ProductName FROM Products WHERE EXISTS     (SELECT * FROM OrderDetails     WHERE     Discount >= 25 AND  Products.ProductID= OrderDetails.ProjectID) ;    
用連接查詢實現如下:    
SELECT   ProductName   FROM    Products , OrderDetails     WHERE Discount >= 25 AND Products.ProductID= OrderDetails.ProjectID    
    (3)字段提取按照“ 需多少,提多少” 的原則,避免“SELECT *”。“SELECT *”需 要數據庫返回相應表的所有列信息,這對於一個列較多的表無疑是一項費時的操作。    
    (4)避免使用!=(或<>)、IS NULL 或IS NOT NULL、IN、NOT IN等這樣的操作符,避免在WHERE 子句中使用非聚合表達式。這些操作符會使系統無法使用索引,而只能直接搜 索表中的數據。例如,SELECT id,name FROM employee WHERE id!=B%      優化器將無法通過索引來確定將要命中的行數,因此需要搜索該表的所有行。    
    (5)避免使用OR,用UNION 代替。OR 語句的執行原理並不是利用列上的索引根據每 個語句分別查找再將結果求並集,而是先取出滿足每個OR 子句的行,存入臨時數據庫的 工作表中,再建立唯一索引以去掉重複行,最後從這個臨時表中計算結果。這樣使用可能 造成索引失效,導致順序掃描整個表,大大降低查詢效率。    
    (6)在執行連接前對關係作適當的預處理,預處理的方法有兩種,在連接屬性上建立 索引和對關係進行排序。    
    (7)將一個大的查詢拆成多步執行查詢。    
    (8)如果應用程序使用循環,可考慮在查詢內放入循環。
6.事務處理調優    
    數據庫的日常運行過程中可能面臨多個用戶同時對數據庫的併發操作帶來的數據不一 致的問題,如:丟失更新、髒讀和不可重複讀等。併發控制的主要方法是封鎖,鎖就是在一段時間內禁止用戶做某些操作以避免產生數據不一致。    
    數據庫應用程序將其工作分成若干個事務進行處理。當一個事務執行時,它訪問數據庫 並執行一些本地計算。開發人員可以假設每一個事務都會被隔離地執行—沒有任何並發動 作。因爲隔離的概念提供了透明性,這種對事務處理方式的保證有時被稱爲原子性保證。但 是,如果把應用程序中的事務序列作爲一個整體來看,則並沒有上面所說的那種保證。在一 個應用程序執行的兩個事務之間,可能會執行另外一個應用程序的事務,而且第二個應用程 序的執行可能修改了第一個應用程序中的兩個事務(或其中的一個)需要訪問的數據項。因 此,事務的長度對保證正確性有着重要影響。    
    儘管將事務切分成較小粒度可以提高執行效率,但會因此破壞執行的正確性。這種性能 和正確性之間的矛盾充斥併發控制的整個調優過程。考慮事務的性能我們要考慮到:事務使 用的鎖的個數(在所有其他條件相同的情況下,使用的鎖個數越少,性能越好);鎖的類型  (讀鎖對性能更有利);事務持有鎖的時間長短(持有時間越短,性能越好)。
    關於鎖的調優 有以下建議:
   (1)使用特殊的系統程序來處理長的讀操作。對於一個只讀的事務R 來說,它“看到” 的數據庫的狀態一直是事務R  開始時的狀態。只讀查詢可以不需要封鎖開銷,在不造成阻 塞和死鎖的情況下,只讀的查詢可以與其他對同一數據進行更新的較小的事務並行地執行。
   (2)消除不必要的封鎖。只有一個事務執行時,或所有事務都是隻讀事務時,用戶應 利用配置選項減少鎖的個數,從而減小鎖管理模塊的內存開銷和執行封鎖操作的處理時間開 銷。
   (3)根據事務的內容將事務切分成較小的事務。事務所要求的鎖越多,它需要等待其 他事務釋放某個鎖的可能就越大。事務T 執行的時間越長,被T  阻塞的事務等待的時間可 能就越長。因此,在可能發生阻塞的情況下,利用較短的事務較好。     
    (4 )在應用程序允許的情況下,適當降低隔離級別。     
    (5)選擇適當的封鎖粒度。頁級封鎖阻止併發事務訪問或修改該頁面上所有記錄,表 級封鎖阻止併發事務訪問或修改表內所有的頁面;記錄級封鎖(行級鎖)比頁級封鎖粒度好, 頁級封鎖比表級封鎖粒度好。長事務(指要訪問表內幾乎所有頁面的事務)應該儘可能使用 表級封鎖來防止死鎖,而短事務應該使用記錄級封鎖來提高併發度。     
    (6)只在數據庫很少被訪問時才修改有關數據定義的數據(系統目錄或元數據)。每個 能夠編譯、添加或刪除表、添加或刪除索引、改變屬性定義的事務都必須訪問目錄數據,因 此,目錄很容易成爲熱點,也因而成爲瓶頸。     
    (7)減少訪問熱點(大量事務訪問和更新的數據)。只有在更新某熱點的事務完成滯後, 其他的事務才能獲得這個熱點上的鎖,因此熱點可能成爲瓶頸。     
    (8)死鎖檢測週期的調優。    
     以上每個建議都可以獨立於其他建議來運用,但是在調優時必須檢測是否能體現合適 的隔離性保證。

7.總結    
    數據庫性能優化的基本原則就是通過儘可能少的磁盤訪問獲得所需要的數據。本文從計 算機硬件、關係系統與應用程序、數據庫索引、SQL  語句、事務處理幾個比較共性的方面 分析了數據庫性能優化的問題,  提出了若干數據庫性能優化的策略。當然實現優化的方法還 有很多,  要根據具體情況而定。對於不同的應用情況,我們應該具體情況具體分析,  各方面優 化措施綜合運用,  以使數據庫性能得到提高。數據庫應用系統的性能是一項全民工程,開發 團隊的所有人都有責任爲性能做貢獻,樹立性能意識,使之成爲日常工作的習慣而不是單獨 成爲某一階段的工作,要未雨綢繆,不要寄希望於某一個環節的工作。


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