在理解Oracle的讀寫操作機(jī)制的基礎(chǔ)上,解決各種常見的IO問題
數(shù)據(jù)庫的作用就是實現(xiàn)對數(shù)據(jù)的管理和查詢。任何一個數(shù)據(jù)庫系統(tǒng),必然存在對數(shù)據(jù)的大量讀或者寫或者兩中操作都大量存在。IO問題也往往是導(dǎo)致數(shù)據(jù)庫性能問題的重要原因。在這篇文章中,主要幫助大家在理解Oracle的讀寫操作機(jī)制的基礎(chǔ)上,靈活解決遇到的各種常見的IO問題。
1 Oracle中IO的產(chǎn)生
IO當(dāng)然包括了讀、寫兩部分,先介紹Oracle中寫操作的產(chǎn)生。
1.1 寫
介紹寫操作之前,先簡單的看下Oracle的物理結(jié)構(gòu):oracle的物理文件包括以下三種文件:控制文件(Control Files)、重做日志文件(Redo Log Files)、數(shù)據(jù)文件(datafiles)。而數(shù)據(jù)文件中,根據(jù)功能的不同,還可以分為系統(tǒng)數(shù)據(jù)文件、臨時空間文件、回滾段文件和用戶數(shù)據(jù)文件。另外,如果數(shù)據(jù)庫的Archive Log模式被激活,還存在歸檔日志文件。Oracle的IO產(chǎn)生,就是對這些文件的數(shù)據(jù)讀、寫操作。下面再詳細(xì)看下幾種主要寫操作的產(chǎn)生及其過程。
1.1.1 控制文件
控制文件中記錄了整個數(shù)據(jù)庫的物理結(jié)構(gòu)信息,如數(shù)據(jù)庫名字、數(shù)據(jù)文件及日志文件名字和位置、事件戳信息等等。任何數(shù)據(jù)庫的結(jié)構(gòu)變化(如果創(chuàng)建新的數(shù)據(jù)文件)都會引起Oracle修改控制文件。同時控制文件還記錄系統(tǒng)和各個數(shù)據(jù)文件的SCN(System Change Number,關(guān)于SCN可以參見文章《Oracle SCN機(jī)制詳解》)信息,以用于數(shù)據(jù)恢復(fù),因此數(shù)據(jù)文件上的SCN變化后,Oracle也會相應(yīng)修改控制文件上的SCN信息。
1.1.2 用戶數(shù)據(jù)修改
由于內(nèi)存的讀寫效率比磁盤的讀寫效率高萬倍,因此,為了降低IO wait,oracle會將數(shù)據(jù)cache在內(nèi)存(Buffer Cache,對Buffer Cache的詳細(xì)介紹可以參見《Oracle內(nèi)存全面分析》)中,對數(shù)據(jù)的讀寫盡量在內(nèi)存中完成。當(dāng)Buffer Cache中的數(shù)據(jù)緩存塊被修改過了,它就被標(biāo)記為“臟”數(shù)據(jù)。根據(jù)LRU(Least Recently Used)算法,如果一個數(shù)據(jù)塊最近很少被使用,它就稱為“冷”數(shù)據(jù)塊。進(jìn)程DBWn(系統(tǒng)中可以存在多個DBW進(jìn)程,n為序號)負(fù)責(zé)將“冷”的“臟”數(shù)據(jù)寫入數(shù)據(jù)文件中去。DBWn進(jìn)程會在以下兩種情況下將“臟”數(shù)據(jù)寫入磁盤中去:
當(dāng)服務(wù)進(jìn)程掃描一定數(shù)量(閥值)的Buffer Cache后還沒有找到干凈、可重用的緩存塊后,它會通知DBWn進(jìn)程將“臟”數(shù)據(jù)寫入文件中去,以釋放出空閑緩存;當(dāng)發(fā)生檢查點(Checkpoint)時。1.1.3 Redo Log
在非直接寫(Direct Write)的情況下,事務(wù)中的寫操作都會產(chǎn)生Redo Log,作為數(shù)據(jù)塊異常關(guān)閉時的恢復(fù)記錄。同樣,和寫用戶數(shù)據(jù)類似,Redo Log也不會被直接寫入Redo Log文件,而是先寫入Log Buffer中。
Log Buffer是一個可以循環(huán)重用的緩存區(qū)。LGWR進(jìn)程負(fù)責(zé)將Log Buffer中的記錄寫入Redo Log File中去。一旦Log Buffer中的條目被寫入了Redo Log文件中,就可以被重用了。
為了保證事務(wù)盡快獲得Log Buffer,LGWR進(jìn)程一般會盡快將Log Buffer中的數(shù)據(jù)寫入Redo Log文件中去。在以下幾種情況下,LGWR回將一個連續(xù)的Log Buffer寫入Redo Log文件中去:
當(dāng)一個事務(wù)提交(COMMIT)時;每3秒鐘寫一次Log Buffer;當(dāng)Log Buffer到達(dá)1/3滿時;當(dāng)DBWn進(jìn)程將“臟”數(shù)據(jù)寫入磁盤時;1.1.4 Archive Log
當(dāng)據(jù)庫的Archive Log模式被激活后,所有Redo Log數(shù)據(jù)都會被寫入Archive Log文件中以便日后進(jìn)行恢復(fù)。當(dāng)發(fā)生日志組切換時,ARCn(Archive進(jìn)程,可以存在多個)進(jìn)程就會Redo Log文件拷貝到指定存儲目錄中去,成為Archive Log文件。
1.1.5 臨時表空間
當(dāng)Oracle在執(zhí)行一些SQL時,會需要一些臨時空間來存儲執(zhí)行語句時產(chǎn)生的中間數(shù)據(jù)。這些臨時空間由Oracle從指定的臨時表空間中分配給進(jìn)程。主要有三種情況會占用臨時空間:臨時表/索引操作、排序和臨時LOB操作。
臨時表/索引
在會話中,當(dāng)?shù)谝淮螌εR時表進(jìn)行INSERT(包括CTAS)時,Oracle會從臨時表空間中為臨時表及其索引分配臨時空間一存儲數(shù)據(jù)。
排序
任何會使用到排序的操作,包括JOIN、創(chuàng)建(重建)INDEX、ORDER BY、聚合計算(GROUP BY)以及統(tǒng)計數(shù)據(jù)收集,都可能使用到臨時表空間。
排序操作首先會選擇在內(nèi)存中的Sort Area進(jìn)行(Sort In Memory),一旦Sort Area不足,則會使用臨時空間進(jìn)行排序操作(Sort In Disk)。看以下例子:
臨時LOB對象
LOB對象包括BLOB、CLOB、NCLOB、和BFILE。在PLSQL程序塊中,如果定義了LOB變量,則這些LOB變量就是臨時LOB對象。臨時LOB對象被創(chuàng)建在臨時表空間上,直到LOB數(shù)據(jù)被釋放,或者會話結(jié)束。
1.1.6 回滾段
我們知道,一個事務(wù)在未被提交前,其做的任何修改都是可以被回滾(Rollback)的。這些回滾數(shù)據(jù)就被放到回滾段(Rollback Segment)上。此外,一致性讀(Read Consistency)、數(shù)據(jù)庫恢復(fù)(Recover)都會用到回滾段。
任何數(shù)據(jù)塊的修改都會被記錄在回滾段中,甚至Redo Log也會產(chǎn)生回滾記錄。當(dāng)任何一個非只讀(只有查詢)的事務(wù)開始時,oracle會自動為其指定下一個可用的回滾段。事務(wù)中任何數(shù)據(jù)變化都被寫入回滾段中。如果事務(wù)回滾,oracle根據(jù)回滾段中的回滾記錄將buffer cache中的“臟”數(shù)據(jù)恢復(fù),釋放回滾段空間。當(dāng)事務(wù)被提交,由于要保證一致性讀,oracle并不會立即釋放回滾段中的數(shù)據(jù),而是會保留一段時間。
1.1.7 Direct-Path Insert
這里,我們還要介紹一種特殊的寫操作——Direct-Path Insert(直接路徑插入)。Direct-Path Insert通過直接在表中已存在的數(shù)據(jù)后面添加數(shù)據(jù),直接將數(shù)據(jù)寫入數(shù)據(jù)文件中,而忽略掉了Buffer Cache。
我們前面提到,為了能在意外時恢復(fù)數(shù)據(jù),每一個數(shù)據(jù)修改都會被記錄到Redo Log中。然而,由于Redo Log需要寫入到物理文件中去,是一個比較消耗性能的操作。為了提高性能,我們在批量寫入數(shù)據(jù)時就可以通過Direct-Path Insert的指定NOLOGING的方式來避免寫Redo Log。
有多種方法可以指定Direct-Path Insert:CTAS(CREATE TABLE AS SELECT);SQL*Loader指定Direct參數(shù);在語句中指定APPEND提示。
1.2???? 讀 1.2.1 物理讀
產(chǎn)生物理讀主要有以下幾種情況:
第一次讀取
當(dāng)數(shù)據(jù)塊第一次被讀取到,Oracle會先將其從磁盤上讀入Buffer Cache中,并將他們放在LRU(Last Recently Used)鏈表的MRU(Most Recently Used)端。再次訪問數(shù)據(jù)塊時就可以直接從Buffer Cache中讀取、修改了。看以下例子:
數(shù)據(jù)塊被重新讀入Buffer Cache
如果有新的數(shù)據(jù)需要被讀入Buffer Cache中,而Buffer Cache又沒有足夠的空閑空間,Oracle就根據(jù)LRU算法將LRU鏈表中LRU端的數(shù)據(jù)置換出去。當(dāng)這些數(shù)據(jù)被再次訪問到時,需要重新從磁盤讀入。
全表掃描
當(dāng)發(fā)生全表掃描(Full Table Scan)時,用戶進(jìn)程讀取表的數(shù)據(jù)塊,并將他們放在LRU鏈表的LRU端(和上面不同,不是放在MRU端)。這樣做的目的是為了使全表掃描的數(shù)據(jù)盡快被移出。因為全表掃描一般發(fā)生的頻率較低,并且全表掃描的數(shù)據(jù)塊大部分在以后都不會被經(jīng)常使用到。
而如果你希望全表掃描的數(shù)據(jù)能被cache住,使之在掃描時放在MRU端,可以通過在創(chuàng)建或修改表(或簇)時,指定CACHE參數(shù)。
1.2.2 邏輯讀
邏輯讀指的就是從(或者視圖從)Buffer Cache中讀取數(shù)據(jù)塊。按照訪問數(shù)據(jù)塊的模式不同,可以分為即時讀(Current Read)和一致性讀(Consistent Read)。注意:邏輯IO只有邏輯讀,沒有邏輯寫。
即時讀
即時讀即讀取數(shù)據(jù)塊當(dāng)前的最新數(shù)據(jù)。任何時候在Buffer Cache中都只有一份當(dāng)前數(shù)據(jù)塊。即時讀通常發(fā)生在對數(shù)據(jù)進(jìn)行修改、刪除操作時。這時,進(jìn)程會給數(shù)據(jù)加上行級鎖,并且標(biāo)識數(shù)據(jù)為“臟”數(shù)據(jù)。
一致性讀
Oracle是一個多用戶系統(tǒng)。當(dāng)一個會話開始讀取數(shù)據(jù)還未結(jié)束讀取之前,可能會有其他會話修改它將要讀取的數(shù)據(jù)。如果會話讀取到修改后的數(shù)據(jù),就會造成數(shù)據(jù)的不一致。一致性讀就是為了保證數(shù)據(jù)的一致性。在Buffer Cache中的數(shù)據(jù)塊上都會有最后一次修改數(shù)據(jù)塊時的SCN。如果一個事務(wù)需要修改數(shù)據(jù)塊中數(shù)據(jù),會先在回滾段中保存一份修改前數(shù)據(jù)和SCN的數(shù)據(jù)塊,然后再更新Buffer Cache中的數(shù)據(jù)塊的數(shù)據(jù)及其SCN,并標(biāo)識其為“臟”數(shù)據(jù)。當(dāng)其他進(jìn)程讀取數(shù)據(jù)塊時,會先比較數(shù)據(jù)塊上的SCN和自己的SCN。如果數(shù)據(jù)塊上的SCN小于等于進(jìn)程本身的SCN,則直接讀取數(shù)據(jù)塊上的數(shù)據(jù);如果數(shù)據(jù)塊上的SCN大于進(jìn)程本身的SCN,則會從回滾段中找出修改前的數(shù)據(jù)塊讀取數(shù)據(jù)。通常,普通查詢都是一致性讀。
1.2.3 查找數(shù)據(jù)
在一個查詢操作中,大量的讀操作都產(chǎn)生于數(shù)據(jù)的查找過程中。減少查找過程是我們優(yōu)化IO性能問題的重要目標(biāo)。
下面介紹幾種主要的數(shù)據(jù)查找方式。
Full Table Scan
當(dāng)查詢條件無法命中任何索引、或者掃描索引的代價大于全表掃描代價的某一比例時(由參數(shù)optimizer_index_cost_adj設(shè)定),Oracle會采用全表掃描的方式查找數(shù)據(jù)。當(dāng)發(fā)生全表掃描時,Oracle會自下向上一次讀取一定數(shù)量(由參數(shù)db_file_multiblock_read_count設(shè)定)的數(shù)據(jù)塊,一直讀取到高水位標(biāo)志(HWM,High Water Mark)下。Full Table Scan會引起db file scattered read事件。
INDEX UNIQUE SCAN
全表掃描查找數(shù)據(jù)的效率是非常低的。而索引能大幅提高查找效率。普通索引的數(shù)據(jù)結(jié)構(gòu)是B-Tree,樹的葉子節(jié)點中包含數(shù)據(jù)的ROWID,指向數(shù)據(jù)記錄,同時還有指針指向前一個/后一個葉子節(jié)點。索引掃描每次讀取一個數(shù)據(jù)塊,索引掃描是“連續(xù)的”(Sequential)。當(dāng)索引為UNIQUE索引時,每個葉子節(jié)點只會指向一條數(shù)據(jù)。如果Oracle能預(yù)知掃描結(jié)果只有0或1條記錄時,會采用INDEX UNIQUE SCAN。當(dāng)對Unique Index中的所有字段進(jìn)行完全匹配時,會發(fā)生INDEX UNIQUE SCAN。
INDEX UNIQUE SCAN的查找過程如下:
從數(shù)的根節(jié)點數(shù)據(jù)塊開始查找;查找根節(jié)點塊中所有key值中大于或等于要查找的值的最小key值;如果key值大于查找值,則繼續(xù)查找這個key值之前一個key值所指向的子節(jié)點數(shù)據(jù)塊;如果key值等于查找值,則繼續(xù)查找這個key值所指向的子節(jié)點數(shù)據(jù)塊;如果沒有key值大于或等于查找值,則繼續(xù)查找最大key值所指向的子節(jié)點數(shù)據(jù)塊;如果繼續(xù)查找的節(jié)點數(shù)據(jù)塊是數(shù)一個分支節(jié)點,則重復(fù)2~4步;如果查找的節(jié)點是葉子節(jié)點數(shù)據(jù)塊,則在數(shù)據(jù)塊中查找等于查找值的key值;如果找到相等的key值,則返回數(shù)據(jù)和ROWID;如果沒找到相等的key值,則說明沒有符合條件的數(shù)據(jù),返回NULL。INDEX RANGE SCAN
如果通過索引查找數(shù)據(jù)時,Oracle認(rèn)為會返回數(shù)據(jù)可能會大于1,會進(jìn)行INDEX RANGE SCAN,例如Unique Index中字段不完全匹配查找時、非Unique Index查找時。
INDEX RANGE SCAN分為閉包(有前后查找邊界)和非閉包(只有一邊或者沒有邊界)。返回數(shù)據(jù)會依據(jù)索引增序排序,多個相同值則會按照ROWID的增序排序。
閉包條件下的INDEX RANGE SCAN的查找過程如下:
從數(shù)的根節(jié)點數(shù)據(jù)塊開始查找;查找根節(jié)點塊中所有key值中大于或等于要查找的起始值的最小key值;如果key值大于起始值,則繼續(xù)查找這個key值之前一個key值所指向的子節(jié)點數(shù)據(jù)塊;如果key值等于起始值,則繼續(xù)查找這個key值所指向的子節(jié)點數(shù)據(jù)塊;如果沒有key值大于或等于起始值,則繼續(xù)查找最大key值所指向的子節(jié)點數(shù)據(jù)塊;如果繼續(xù)查找的節(jié)點數(shù)據(jù)塊是數(shù)一個分支節(jié)點,則重復(fù)2~4步;如果查找的節(jié)點是葉子節(jié)點數(shù)據(jù)塊,則在數(shù)據(jù)塊中大于或等于要查找的起始值的最小key值;如果Key值小于或等于結(jié)束值,則:如果所有Key字段都符合WHERE字句中的查找條件,則返回數(shù)據(jù)和ROWID;否則繼續(xù)查找當(dāng)前葉子節(jié)點所指向的右邊的葉子節(jié)點。
INDEX UNIQUE SCAN和INDEX RANGE SCAN都會引起db file sequential read事件。
TABLE ACCESS BY INDEX ROWID
當(dāng)發(fā)生索引掃描時,如果需要返回的字段都在索引上,則直接返回索引上的數(shù)據(jù),而如果還需要返回非索引上的字段的值,Oracle則需要根據(jù)從索引上查找的ROWID到對應(yīng)的數(shù)據(jù)塊上取回數(shù)據(jù),這時就是TABLE ACCESS BY INDEX ROWID。
INDEX FAST FULL SCAN & INDEX FULL SCAN
索引快速全掃描和全表掃描類似,一次讀取db_file_multiblock_read_count個數(shù)據(jù)塊來描所有索引的葉子節(jié)點。INDEX FAST FULL SCAN和其他索引掃描不同,它不會從樹的根節(jié)點開始讀取,而是直接掃描所有葉子節(jié)點;也不會一次讀取一個數(shù)據(jù)塊,而是一次讀取db_file_multiblock_read_count個數(shù)據(jù)塊。INDEX FAST FULL SCAN會引起db file scattered read事件。
在某些情況下,如db_file_multiblock_read_count值過小、強(qiáng)制使用索引掃描時,會發(fā)生INDEX FULL SCAN。INDEX FULL SCAN和INDEX FAST FULL SCAN不同,它是一種索引掃描,按照B-Tree的查找法從樹的根節(jié)點開始掃描,遍歷整棵樹,并且一次讀取一個數(shù)據(jù)塊。它會引起db file sequential read事件。
2 IO系統(tǒng)的設(shè)計和配置
要控制好數(shù)據(jù)庫的整體IO性能,在規(guī)劃數(shù)據(jù)庫架構(gòu)時就需要做好IO系統(tǒng)的設(shè)計和配置。例如,將對IO要求不同的文件放置在不同的存儲設(shè)備上;規(guī)劃數(shù)據(jù)文件的分布、均衡IO負(fù)擔(dān)等。
2.1???? OS和存儲相關(guān)
IO性能是直接和操作系統(tǒng)已經(jīng)硬件性能相關(guān)的。如果能利用操作系統(tǒng)的一些高級IO特性,或者采用更高速的磁盤設(shè)備,能大大提高IO性能。下面介紹一些OS的IO配置、不同的磁盤硬件設(shè)備以及存儲技術(shù)。
2.1.1 文件系統(tǒng)(File System)和裸設(shè)備(Raw Device)
我們知道,內(nèi)存的讀寫效率比磁盤高近萬倍,因此Oracle在內(nèi)存中開辟了一片區(qū)域,稱為Buffer Cache,使數(shù)據(jù)的讀寫盡量在Buffer Cache中完成。同樣,在文件系統(tǒng)中,操作系統(tǒng)為了提高讀寫效率,也會為文件系統(tǒng)開辟一塊Buffer Cache用于讀寫數(shù)據(jù)的緩存。這樣,Oracle的數(shù)據(jù)會被緩存2次。為了避免OS的這次緩存,我們可以采用裸設(shè)備做為數(shù)據(jù)文件的存儲設(shè)備。裸設(shè)備,也稱為裸分區(qū)(Raw Partition),它是一個沒有被加載(Mount)到操作系統(tǒng)的文件系統(tǒng)上、也沒有加載到Oracle集群文件系統(tǒng)(OCFS Oracle Cluster File System)的磁盤分區(qū),它通過字符設(shè)備驅(qū)動來訪問。裸設(shè)備的文件讀寫不由操作系統(tǒng)控制,而是由應(yīng)用程序(如Oracle RDBMS)直接控制。
2.1.2 IO方式
OS和文件系統(tǒng)對IO的控制存在多種方式,不同的IO方式下對于數(shù)據(jù)庫的IO性能影響也不同。
2.1.2.1? Direct IO & Concurrent IO
除了裸設(shè)備,某些文件系統(tǒng)可以支持Direct IO,以避開讀寫緩沖。如果要使用Direct IO,需要指定Oracle參數(shù)“filesystemio_options”來設(shè)置支持Direct IO。但是要注意,不同OS中的不同文件系統(tǒng)對Direct IO的支持也不同:
Windows ??? 在windows中不需要做特別設(shè)置可以直接使用Direct IO;AIX ?? 在AIX中,JFS文件系統(tǒng)需要通過設(shè)置“filesystemio_options”為“SETALL”或者“DIRECTIO”來支持Direct IO;LINUX Linux在內(nèi)核版本為2.4.9以上才支持Direct IO。NFS或者OCFS文件系統(tǒng)支持Direct IO。需要設(shè)置“filesystemio_options”為“SETALL”或者“DIRECTIO”;Solaris???? Solaris需要在操作系統(tǒng)中設(shè)置“forcedirectio”選項,并設(shè)置“filesystemio_options”為“SETALL”或者“DIRECTIO”。
參數(shù)“filesystemio_options”支持4種值:
ASYNCH: 使Oracle支持文件的異步(Asynchronous)IO;DIRECTIO:使Oracle支持文件的Direct IO;SETALL:使Oracle同時支持文件的Asynchronous IO和Direct IO;NONE:使Oracle關(guān)閉對Asynchronous IO和Direct IO的支持。
在AIX的JFS2文件系統(tǒng)上,如果“filesystemio_options”為“SETALL”,則會支持Concurrent IO。CIO比DIO的性能更高,因為JFS2的CIO支持多個進(jìn)程同時對一個文件進(jìn)行讀寫。
2.1.2.2? Asynchronous IO & Synchronous IO
通常,用的比較多的IO模型是同步IO(Synchronous IO)。在這種模式下,當(dāng)請求發(fā)出之后,應(yīng)用程序就會阻塞,直到請求滿足為止。這種模式最大好處就是調(diào)用應(yīng)用程序在等待 I/O 請求完成時不需要使用CPU資源。但是,對于一些強(qiáng)調(diào)高響應(yīng)速度的程序(如DB)來說,希望這種等待時間越短越好,我們這時就可以考慮采用異步IO(Asynchronous IO)模式。異步IO模式下,進(jìn)程發(fā)出IO請求后無需等待IO完成,可以去處理其它事情;IO請求被放入一個隊列中,一旦IO完成,系統(tǒng)會發(fā)出信號通知進(jìn)程。
異步IO可以使需要大量寫的Oracle進(jìn)程(如DBWn進(jìn)程)將IO請求隊列化,以充分利用硬件的IO帶寬,從而使它們能最大程度實現(xiàn)并行處理。異步IO還可以使那些需要進(jìn)行大量計算的操作(如排序)在它們發(fā)出IO請求前預(yù)先從磁盤取出數(shù)據(jù),以使IO和計算并行處理。
確認(rèn)操作系統(tǒng)已經(jīng)設(shè)置支持AIO后,還需要設(shè)置Oracle初始化參數(shù)"DISK_ASYNCH_IO"為“true”以支持異步IO。
2.1.3 負(fù)載均衡及條帶化(Striping)
當(dāng)多個進(jìn)程同時訪問一個磁盤時,會出現(xiàn)磁盤沖突。大多數(shù)磁盤系統(tǒng)都對訪問次數(shù)(每秒的IO操作)和數(shù)據(jù)傳輸率(每秒傳輸?shù)臄?shù)據(jù)量)有限制。當(dāng)達(dá)到這些限制時,后面要訪問磁盤的進(jìn)程就需要等待,這時就是所謂的磁盤沖突。
避免磁盤沖突是優(yōu)化IO性能的一個目標(biāo),這就需要將一個熱點磁盤上的IO訪問負(fù)載分擔(dān)到其他可用磁盤上,也就是IO負(fù)載均衡。在一些成熟的磁盤負(fù)載均衡技術(shù)出現(xiàn)之前,DBA需要了解/預(yù)測各系統(tǒng)的IO負(fù)載量,通過手工配置每個數(shù)據(jù)到不同存放位置以分擔(dān)IO負(fù)載來達(dá)到負(fù)載均衡的目的。
條帶化技術(shù)就是將數(shù)據(jù)分成很多小部分并把他們分別存儲到不同磁盤上的不同文件中去。這就能使多個進(jìn)程同時訪問數(shù)據(jù)的多個不同部分而不會造成磁盤沖突。很多操作系統(tǒng)、磁盤設(shè)備供應(yīng)商、各種第三方軟件都能做到條帶化。通過條帶化,DBA可以很輕松的做到IO負(fù)載均衡而無需去手工配置。
2.1.4 RAID
RAID的全稱是獨立磁盤冗余陣列(Redundant Array of Independent Disks)。它通過將多個相對比較便宜的磁盤組合起來,并相互連接,同時都連到一個或多個計算機(jī)上,以組成一個磁盤組,使其性能和容量達(dá)到或超過一個價格更昂貴的大型磁盤。RAID分為6級。
RAID-0
RAID-0只提供純粹的條帶化(Stripping)。條帶可以使一個大文件被多個磁盤控制器同時訪問,因此支持對數(shù)據(jù)的并發(fā)訪問。RAID-0不提供數(shù)據(jù)冗余和奇偶保護(hù),它只關(guān)注性能。如果RAID-0中任何一個磁盤出錯,整個數(shù)據(jù)庫都會崩潰。
RAID-1
RAID-1提供磁盤鏡像(Disk Mirror)。在RAID-1中,所有數(shù)據(jù)都會被寫入兩個獨立的磁盤中,以實現(xiàn)對數(shù)據(jù)的冗余保護(hù)。兩塊磁盤的數(shù)據(jù)是同時寫入的,以保證其速度不會低于寫入單獨磁盤的速度。RAID-1實現(xiàn)了數(shù)據(jù)的完全冗余,它提供了所有RAID級別中最安全可靠的數(shù)據(jù)保護(hù)。在這種模式下,寫的性能下降了,但讀的性能被提升了。此外,RAID-1也是最占用磁盤空間的模式
RAID 0+1
RAID-0能提供更好的性能,RAID-1提供最佳的數(shù)據(jù)保護(hù)。如果把兩者結(jié)合在一起就能同時提供高性能和數(shù)據(jù)保護(hù),但是也會同時提高磁盤陣列造價。
RAID-3
在RAID-3中,會有一塊專門的磁盤驅(qū)動被用作存儲錯誤修正或者奇偶校驗數(shù)據(jù)。而其他的磁盤驅(qū)動則被條帶化。RAID-3的并行處理能力比較低,它適合于主要是讀操作的系統(tǒng)(如決策分析系統(tǒng) DSS,但是DSS會存在大量復(fù)雜查詢,需要做JOIN,同樣也會存在一些臨時的寫操作),不適合存在大量寫操作的系統(tǒng)(OLTP)。
RAID-5
RAID-5不做全磁盤鏡像,但它會對每一個寫操作做奇偶校驗計算并寫入奇偶校驗數(shù)據(jù)。奇偶校驗磁盤避免了像RAID-1那樣完全重復(fù)寫數(shù)據(jù)。當(dāng)一個磁盤失效,校驗數(shù)據(jù)被用來重建數(shù)據(jù),從而保證系統(tǒng)不會崩潰。為避免磁盤瓶頸,奇偶校驗和數(shù)據(jù)都會被分布到陣列中的各個磁盤。盡管讀的效率提高了,但是RAID-5需要為每個寫操作做奇偶校驗,因此它的寫的效率很差。
RAID-S
RAID-S是EMC公司的RAID-5的實施方案,它和純粹的RAID-5存在以下區(qū)別:
(1) 它條帶化奇偶校驗,但不條帶化數(shù)據(jù);
(2) 它與一個帶有寫緩存的異步硬件環(huán)境合并。
這個緩存主要是一種延遲寫的機(jī)制,因此它能讓系統(tǒng)在相對不忙的時候計算和寫奇偶校驗信息。
RAID-7
RAID-7也同樣引入了緩存機(jī)制,這個緩存是被一個內(nèi)嵌式操作系統(tǒng)控制。但是,RAID-7中數(shù)據(jù)是被條帶化的,而奇偶校驗不被條帶化。奇偶校驗信息被存放著一個或者多個專門的磁盤上。
2.1.5 SAN
SAN(Storage?Area?Network,存儲區(qū)域網(wǎng))是一個高速的子網(wǎng),這個子網(wǎng)中的設(shè)備可以從你的主網(wǎng)卸載流量。通常SAN由RAID陣列連接光纖通道(Fibre?Channel)組成,SAN和服務(wù)器和客戶機(jī)的數(shù)據(jù)通信通過SCSI命令而非TCP/IP,數(shù)據(jù)處理是“塊級”(block?level)。
SAN通過特定的互連方式連接的若干臺存儲服務(wù)器組成一個單獨的數(shù)據(jù)網(wǎng)絡(luò),提供企業(yè)級的數(shù)據(jù)存儲服務(wù)。?SAN是一種特殊的高速網(wǎng)絡(luò),連接網(wǎng)絡(luò)服務(wù)器和諸如大磁盤陣列或備份磁帶庫的存儲設(shè)備,SAN置于LAN之下,而不涉及LAN。利用SAN,不僅可以提供大容量的存儲數(shù)據(jù),而且地域上可以分散,并緩解了大量數(shù)據(jù)傳輸對于局域網(wǎng)的影響。SAN的結(jié)構(gòu)允許任何服務(wù)器連接到任何存儲陣列,不管數(shù)據(jù)置放在哪里,服務(wù)器都可直接存取所需的數(shù)據(jù)。
2.1.6 NAS
NAS是Network Attached Storage(網(wǎng)絡(luò)附加存儲)的簡稱。在NAS存儲結(jié)構(gòu)中,存儲系統(tǒng)不再通過I/O總線附屬于某個服務(wù)器或客戶機(jī),而直接通過網(wǎng)絡(luò)接口與網(wǎng)絡(luò)直接相連,由用戶通過網(wǎng)絡(luò)訪問。它是連接到一個計算機(jī)網(wǎng)絡(luò)的文件層的數(shù)據(jù)存儲,它可以為不同網(wǎng)絡(luò)客戶端提供數(shù)據(jù)存儲服務(wù)。NAS的硬件與傳統(tǒng)的專用文件服務(wù)器相似。它們的不同點在于軟件端。NAS中的操作系統(tǒng)和其他軟件只提供數(shù)據(jù)存儲、數(shù)據(jù)訪問功能,以及對這些功能的管理。與傳統(tǒng)以服務(wù)器為中心的存儲系統(tǒng)相比,數(shù)據(jù)不再通過服務(wù)器內(nèi)存轉(zhuǎn)發(fā),直接在客戶機(jī)和存儲設(shè)備間傳送,服務(wù)器僅起控制管理的作用。
2.2???? IO配置
在借助各種成熟的存儲技術(shù)的基礎(chǔ)上,合理配置系統(tǒng)的IO分布及系統(tǒng)IO配置能大量減少系統(tǒng)在生產(chǎn)運行中出現(xiàn)IO性能及相關(guān)問題的幾率。當(dāng)然,這些配置是我們在布置數(shù)據(jù)庫系統(tǒng)時初始建議,對于復(fù)雜的系統(tǒng)來說,很多配置(如一些存儲相關(guān)的參數(shù))是需要根據(jù)系統(tǒng)的運行狀況進(jìn)行調(diào)優(yōu)的。
在數(shù)據(jù)庫系統(tǒng)中,如果某個文件或者某塊磁盤上存在遠(yuǎn)遠(yuǎn)高于其他文件或磁盤的大量IO訪問,我們就稱這個文件或磁盤為熱點文件/磁盤。我們在做IO規(guī)劃時的一個重要目標(biāo)就是要消除系統(tǒng)中熱點文件/磁盤的存在,使整個系統(tǒng)的IO負(fù)載相對平衡。
2.2.1 條帶化的設(shè)置
由于現(xiàn)在的存儲技術(shù)成熟、成本降低,大多數(shù)系統(tǒng)都采用條帶化來實現(xiàn)系統(tǒng)的IO負(fù)載分擔(dān)。如果操作系統(tǒng)有LVM(Logical Volume Manager邏輯卷管理器)軟件或者硬件條帶設(shè)備,我們就可以利用這些攻擊來分布IO負(fù)載。當(dāng)使用LVM或者硬件條帶時,決定因素是條帶深度(stripe depth)和條帶寬度(stripe width):
條帶深度指的是條帶的大小,也叫條帶單元;條帶寬度指的是條帶深度的產(chǎn)量或者一個條帶集中的驅(qū)動數(shù);
需要根據(jù)系統(tǒng)的IO要求來合理的選擇這些數(shù)據(jù)。對于Oracle數(shù)據(jù)庫系統(tǒng)來數(shù),比較合理的條帶深度是從256K到1M。下面分析影響條帶深度和條帶寬度的影響因素。
2.2.1.1? 條帶深度
為了提高IO效率,我們要盡量使一次邏輯IO請求由一塊磁盤的一次物理IO請求。因而影響條帶的一個重要因素就是一次邏輯IO請求的大小。
此外,系統(tǒng)中IO的并發(fā)度不同我們對條帶的配置要求也不同。例如,在高并發(fā)度且IO請求的大小都比較小的情況下,我們希望一塊磁盤能同時響應(yīng)多個IO操作;而在那些存在大IO請求的低并發(fā)度系統(tǒng)中,我們可能就需要多塊磁盤同時響應(yīng)一個IO請求。無論是一個磁盤還是多個磁盤響應(yīng)IO請求,我們的一個原則是讓一次邏輯IO能被一次處理完成。
下面先看下影響IO大小的操作系統(tǒng)和Oracle的相關(guān)參數(shù):
db_block_size:Oracle中的數(shù)據(jù)塊大小,也決定了Oracle一次單個IO請求中的數(shù)據(jù)塊的大??;db_file_multiblock_read_count:在多數(shù)據(jù)塊讀時,一次讀取數(shù)據(jù)塊的數(shù)量,它和參數(shù)db_block_size一起決定了一次多數(shù)據(jù)塊讀的大小,它們的乘積不能大于操作系統(tǒng)的最大IO大小;操作系統(tǒng)的數(shù)據(jù)塊大小:這個參數(shù)決定拉Redo Log和Archive Log操作時的數(shù)據(jù)塊大小,對于大多數(shù)Unix系統(tǒng)來說,該值為512K;最大操作系統(tǒng)IO大小:決定了一次單個的IO操作的IO大小的上限,對于大多數(shù)Unix系統(tǒng)來說,由參數(shù)max_io_size設(shè)置;sort_area_size:內(nèi)存中sort area的大小,也決定了并發(fā)排序操作時的IO大?。籬ash_area_size:內(nèi)存中hash area的大小,也決定了哈希操作的IO大小。
其中,前面兩個是最關(guān)鍵的兩個參數(shù)。
在OLTP系統(tǒng)中,會存在大量小的并發(fā)的IO請求。這時就需要考慮選擇比較大的條帶深度。使條帶深度大于IO大小就稱為粗粒度條帶(Coarse Grain Striping)。在高并行度系統(tǒng)中,條帶深度為(n * db_block_size),其中n為大于1的整數(shù)。
通過粗粒度條帶能實現(xiàn)最大的IO吞吐量(一次物理IO可以同時響應(yīng)多個并發(fā)的邏輯IO)。大的條帶深度能夠使像全表掃描那樣的多數(shù)據(jù)塊讀操作由一個磁盤驅(qū)動來響應(yīng),并提高多數(shù)據(jù)塊讀操作的性能。
在低并發(fā)度的DSS系統(tǒng)中,由于IO請求比較序列化,為了避免出現(xiàn)熱點磁盤,我們需要避免邏輯IO之由一塊磁盤處理。這是,粗粒度條帶就不適合了。我們選擇小的條帶深度,使一個邏輯IO分布到多個磁盤上,從而實現(xiàn)IO的負(fù)載均衡。這就叫細(xì)粒度條帶。條帶深度的大小為(n * db_block_size),其中n為小于多數(shù)據(jù)塊讀參數(shù)(db_file_multiblock_read_count)大小的整數(shù)。
另外,IO過程中,你無法保證Oracle數(shù)據(jù)塊的邊界能和條帶單元的大小對齊。如果條帶深度大小和Oracle數(shù)據(jù)塊大小完全相同,而它們的邊界沒有對齊的話,那么就會存在大量一個單獨的IO請求被兩塊磁盤來完成。
在OLTP系統(tǒng)中,為了避免一個邏輯IO請求被多個物理IO操作完成,條帶寬度就需要設(shè)置為兩倍或者兩倍以上于Oracle數(shù)據(jù)塊大小。例如,如果條帶深度是IO大小的N倍,對于大量并發(fā)IO請求,我們可以保證最少有(N-1)/ N的請求是由一塊磁盤來完成。
2.2.1.2? 條帶寬度
正如我們前面所述,無論是一個還是多個磁盤響應(yīng)一個邏輯IO,我們都要求IO能被一次處理。因而在確定了條帶深度的基礎(chǔ)上,我們需要保證條帶寬度 >= IO請求的大小 / 條帶深度。
此外,考慮到以后系統(tǒng)容量的擴(kuò)充,我們也需要規(guī)劃好條帶寬度。
如今大多數(shù)LVM都支持在線動態(tài)增加磁盤。也就是在磁盤容量不足時,我們可以隨時將新磁盤加入到一個已經(jīng)使用的邏輯卷中。這樣的話,我們在設(shè)置邏輯卷時就可以簡單地將所有磁盤都?xì)w入到一個卷中去。
但是,有些LVM可能還不支持動態(tài)增加磁盤。這時我們就需要考慮以后的容量擴(kuò)充對IO均衡的影響了。因為你新增加的磁盤無法加入原有卷,而需要組成一個新的卷。但一般擴(kuò)充的容量和原有容量比較相對比較小,如果原有卷的條帶寬度比較大的話,新增加的卷的條帶寬度無法達(dá)到其大小,這樣就會使新、舊卷之間出現(xiàn)IO失衡。
例如,一個系統(tǒng)的初始配置是一個包含64塊磁盤、每塊磁盤大小為16G的單一邏輯卷。磁盤總的大小是1T。隨著數(shù)據(jù)庫的數(shù)據(jù)增長,需要增加80G的空間。我們把新增加的5個16G磁盤再組成一個邏輯卷。這樣就會導(dǎo)致兩個卷上的IO失衡。為了避免這種情況。我們可以將原有磁盤配置成每個條帶寬度為8個磁盤的8個邏輯卷,這樣在新增加磁盤時可以也增加為8個磁盤的新卷。但必須要保證8個磁盤的條帶寬度能夠支持系統(tǒng)的每秒IO吞吐量。
如果你的條帶寬度設(shè)置得比較小,就需要估算出你的各個數(shù)據(jù)庫文件的IO負(fù)載,并根據(jù)負(fù)載量不同將他們分別部署到不同卷上一分擔(dān)IO負(fù)載。
2.2.2 人工條帶
如果系統(tǒng)不支持LVM或者硬件條帶,IO負(fù)載就必須由DBA根據(jù)數(shù)據(jù)庫文件的IO負(fù)載不同手工將他們分散到各個磁盤上去以保證整個系統(tǒng)的IO負(fù)載均衡。
有許多DBA會將哪些使用頻率非常高的表和它的索引分開存儲。但實際上這種做法并不正確。在一個事務(wù)中,索引會先被讀取到然后再讀取表,它們的IO操作是有前后順序的,因此索引和表存儲在同一個磁盤上是沒有沖突的。僅僅因為一個數(shù)據(jù)文件即包含了索引又包含了數(shù)據(jù)表而將它分割是不可取的。我們需要根據(jù)文件上的IO負(fù)載是否已經(jīng)影響到了數(shù)據(jù)庫的性能來決定是否將數(shù)據(jù)文件分割。
為了正確分布文件,我們首先必須先了解各個數(shù)據(jù)庫文件的IO負(fù)載需求以及IO系統(tǒng)的處理能力。鑒定出每個文件的IO吞吐量。找出哪些文件的IO吞吐率最高而哪些IO量很少,將它們分散分布到所有磁盤上去以平衡IO吞吐率。
如果你不了解或者無法預(yù)計文件的IO負(fù)載,就只能先估計他們的IO負(fù)載來規(guī)劃文件分布,在系統(tǒng)運行過程中再做調(diào)整。
2.2.3 文件分離
無論是采用操作系統(tǒng)條帶化還是手工IO分布方式,如果IO系統(tǒng)或者IO規(guī)劃布置無法滿足IO吞吐率的要求,我們就需要考慮將高IO吞吐率的文件和其他文件分離。我們可以在存儲規(guī)劃階段或者系統(tǒng)運行階段找出那樣的文件。
除了IO吞吐率,在決定是否分割文件時,我們還需要考慮可恢復(fù)性以及數(shù)據(jù)容量擴(kuò)張問題。
但是在分割文件之前,一定要確認(rèn)存在IO瓶頸,然后再根據(jù)產(chǎn)生IO瓶頸的數(shù)據(jù)定位到存在高IO吞吐率的文件(熱點文件)。
2.2.3.1? 表、索引和臨時表空間
如果具有高IO吞吐率的數(shù)據(jù)文件屬于包含表和索引的表空間,我們就需要找出這些文件的IO是否可以通過SQL語句調(diào)優(yōu)或者優(yōu)化應(yīng)用程序來降低。
如果具有高IO吞吐率的數(shù)據(jù)文件屬于臨時表空間,那我們就需要檢查是否可以通過避免或調(diào)優(yōu)SQL語句的排序操作來降低IO。
經(jīng)過應(yīng)用調(diào)優(yōu)后,如果IO分布仍然無法滿足IO吞吐的要求,我們就需要考慮分離高IO吞吐率的數(shù)據(jù)文件了。
2.2.3.2? Redo Log文件
如果具有高IO吞吐率的文件是Redo Log文件,則需要考慮將Redo Log文件與其他文件分離,可以通過以下配置來實現(xiàn):
將所有Redo Log文件放到?jīng)]有任何其他文件的磁盤上去。考慮到可恢復(fù)性,需要將一個Redo Log組中的成員文件分別放到不同的物理磁盤上去;將每個Redo Log組放到一個沒有任何其他文件的單獨磁盤上;通過操作系統(tǒng)條帶化工具,將Redo Log文件條帶化分布到多個磁盤上去;不要將Redo Log文件放到RAID 5上去
Redo Log文件是由LGWR進(jìn)程序列化的寫入的。如果在同一個磁盤上不存在并發(fā)的其他IO操作,寫入效率就更高。我們需要確認(rèn)已經(jīng)沒有其他優(yōu)化調(diào)整空間再考慮分割Redo Log文件。如果系統(tǒng)支持AIO但還沒有激活該特性,可以考慮激活A(yù)IO看是否能解決Redo Log的IO性能瓶頸。
2.2.3.3? 歸檔Redo Log
如果歸檔變慢,我們也許可以通過使LGWR的寫操作與Archive進(jìn)程的讀操作分離來避免LGWR進(jìn)程魚Archive進(jìn)程直接的IO沖突。我們可以同交替成組存放Redo Log文件來實現(xiàn)。
例如,我們有四組Redo Log,每組包含兩個Log文件:(A1,A2)、(B1,B2)、(C1,C2)、(D1,D2)。我們就可以以下面這種存放方式將它們分布存儲到四個磁盤上去來實現(xiàn)磁盤分離訪問:(A1,C1)、(A2、C2)、(B1,D1)、(B2,D2)。
當(dāng)LGWR進(jìn)程做日志切換時,如從A組切換到B組,LGWR開始向B組寫Redo Log(第三、四塊磁盤),而Archive進(jìn)程則從B組讀取數(shù)據(jù)(第一、二塊磁盤)寫入歸檔文件中去,他們分別訪問的是不同磁盤,因而避免了IO沖突。
2.3???? 三種簡單的配置方法
這里給出三種簡單的操作系統(tǒng)IO配置的例子,包括如何簡單地計算來決定磁盤的拓?fù)浣Y(jié)構(gòu)、條帶深度等等。
2.3.1 將所有文件條帶化到所有磁盤上去
IO配置最簡單的方法就是建立一個大的邏輯卷,將所有磁盤都條帶化到這個卷中去??紤]到可恢復(fù)性,這個卷需要被鏡像(RAID 1)。每個磁盤的條帶深度必須大于頻繁執(zhí)行的IO操作的最大IO大小。這種配置對大多數(shù)情況都能提供足夠的性能支持。
2.3.2 將歸檔日志放到另外的磁盤上去
在歸檔模式下,如果歸檔文件也和其他文件放在同一個條帶化的卷中,那么當(dāng)歸檔進(jìn)程對Redo Log進(jìn)行歸檔時,會大大增加磁盤的IO負(fù)載。將歸檔日志轉(zhuǎn)移到其他磁盤上有如下好處:
歸檔進(jìn)程效率提高;當(dāng)歸檔時,其他進(jìn)程受到歸檔進(jìn)程的影響
歸檔日志的磁盤數(shù)由歸檔日志產(chǎn)生的頻率以及歸檔存儲容量決定。
2.3.3 將Redo Log文件放到另外的磁盤上去
在更新非常頻繁的OLTP系統(tǒng)中,Redo Log的寫操作非常頻繁。將Redo Log文件轉(zhuǎn)移到其他磁盤上可以有如下好處:
寫Redo Log的讀寫效率最高,因而事務(wù)的執(zhí)行也能獲得最佳性能;寫Redo Log操作不會影響任何其他IO操作
Redo Log的磁盤數(shù)量有Redo Log的大小決定。由于現(xiàn)在的磁盤容量都非常大,通常配置兩個磁盤(如果做鏡像則需要四塊)就足夠了。并且,根據(jù)我們前面的分析,將Redo Log文件交互的存放到兩塊磁盤上去能避免LGWR進(jìn)程的寫操作與ARCH進(jìn)程的讀操作之間的IO沖突。
3 Oracle中的IO問題及其解決思路
對于負(fù)載偏重點不同,我們可以簡單的將數(shù)據(jù)庫系統(tǒng)分為CPU負(fù)載系統(tǒng)(CPU Bound System)和IO負(fù)載系統(tǒng)(IO Bound System)。顧名思義,CPU負(fù)載系統(tǒng)的資源瓶頸在于CPU,而IO負(fù)載系統(tǒng)的瓶頸在于磁盤IO。
我們可以通過操作系統(tǒng)的一些命令來確認(rèn)一個系統(tǒng)是否是存在IO負(fù)載。在UNIX下,可以使用"iostat"或者"sar -d"來看系統(tǒng)的IO情況;在windows下,可以通過系統(tǒng)的性能監(jiān)視器查看,但由于性能監(jiān)控器中看到的IO是靜態(tài)的IO總量信息,并不直觀,因此也可以用本站的TopShow工具來查看實時的IO信息。
在UNIX系統(tǒng)下,發(fā)現(xiàn)CPU IDLE很低并不一定代表這是一個CPU負(fù)載系統(tǒng)。一個IO負(fù)載系統(tǒng)在表面上看CPU的IDLE值也可能很低:
oracle@db01:/export/home/oracle>?sar?-u?1?10
?
HP-UX?hkhpdv45?B.11.23?U?ia64????10/24/07
?
09:43:05????%usr????%sys????%wio???%idle
09:43:06?43?25?30??1
09:43:07?44?36?19?1
09:43:08?23?27?44??6
09:43:09?12?37?50?1
09:43:10?1036?51?3
09:43:11?15?34?42??9
09:43:12?18?36?44?3
09:43:13?17?35?46?2
09:43:14?12?32?52?4
09:43:15?12?31?56?1
??
Average??21?33?43??3
我們可以注意到,實際上WIO是引起CPU IDLE過低的主要原因。WIO是當(dāng)一個進(jìn)程需要運行或已經(jīng)運行后,因為需要等待IO事件而被阻塞了。事實上CPU是處于IDLE狀態(tài)(在某些系統(tǒng)中,已經(jīng)將WIO取消并歸為IDLE),真正的原因是系統(tǒng)中存在IO瓶頸。
通過iostat或者sar -d我們可以找出存在IO瓶頸的磁盤設(shè)備,如果該磁盤設(shè)備是用于Oracle 數(shù)據(jù)庫存儲文件的,我們可以判斷出是數(shù)據(jù)庫存在IO問題。在windows下,可以通過TopShow來找出哪個進(jìn)程正在進(jìn)行大量IO傳輸,如果是Oracle進(jìn)程,也可以判斷為是數(shù)據(jù)庫存在IO問題。
確認(rèn)系統(tǒng)存在IO問題后,我們就需要定位到底是什么引起的IO問題,該采取什么措施來解決問題。根據(jù)我們前面的介紹,Oracle中存在各種IO,要定位IO,最好的工具是statspack(在10g以后,可以用AWR)。通過statspack report的Top 5 Events,我們可以看到對系統(tǒng)系能影響最大的5個等待event,而不同的IO問題會對應(yīng)不同Event,所以,我們可以根據(jù)這些event采取不同的措施來解決IO問題。下面是一個典型的IO負(fù)載系統(tǒng)的Top 5 Event:
Top?5?Timed?Events
~~~~~~~~~~~~~~~~~~?????%?Total
Event??Waits????Time?(s)?Ela?Time
--------------------------------------------?------------?-----------?--------
db?file?sequential?read???70,575,969?????344,200????53.34
db?file?scattered?read?11,240,748?????163,242????25.30
log?file?sync?????657,241?36,363?????5.64
CPU?time??35,290?????5.47
log?file?parallel?write???833,799?20,767?????3.22
可以看到,前兩個時間“db file sequential read”和“db file scattered read”分別占了總等待時間的53.34%和25.30%,而我們前面提到這兩個事件分別是由索引掃面和全表掃面(或快速索引掃面)引起的,因此,能解決索引掃面問題和全表掃面問題就能解決這個系統(tǒng)的IO瓶頸。
IO問題到底對CPU有多大影響呢?我們用以上例子中的數(shù)據(jù)分析一下。從等待時間統(tǒng)計數(shù)據(jù)中,我們看到的是時間在總等待時間中所占的比例。而系統(tǒng)的“總響應(yīng)時間 ”= “等待時間 ”+ “CPU工作時間”(注意,上面Top 5事件中的“CPU Time”不是指CPU的工作時間,而是指CPU的等待時間)。“CPU工作時間”的數(shù)據(jù)我們可以在“Instance Activities Stats for DB”這一分類統(tǒng)計數(shù)據(jù)中找到:
Statistic?Total?????per?Second????per?Trans
---------------------------------?------------------?--------------?------------
CPU?used?by?this?session??17,136,868??396.7?15.5
先計算出“總等待時間” = 344,200 * 100% / 53.34% = 645,294s
“總響應(yīng)時間” = “總等待時間” + “CPU工作時間” = 645,294 + 17,136,868 = 17,782,162s
我們可以算出“CPU工作時間”、“db file sequential read”和“db file scattered read”分別在“總響應(yīng)時間中所占的比例為:
CPU工作時間 = 17,136,868 / 17,782,162 = 96.4%
“db file sequential read” = 344,200 / 17,782,162 = 1.9%
“db file scattered read” = 163,242 / 17,782,162 = 0.9%
可見,IO事件所引起的等待時間在總響應(yīng)時間所占比例并不大。因此,我們在做系統(tǒng)優(yōu)化之前先分析系統(tǒng)是CPU負(fù)載系統(tǒng)還是IO負(fù)載系統(tǒng)對于我們的優(yōu)化方向和最終的優(yōu)化效果起很大的作用。
以下事件是可能由IO問題引起的等待事件,在IO負(fù)載系統(tǒng)中,我們要特別關(guān)注這些事件:
與數(shù)據(jù)文件相關(guān)的IO事件
'db file sequential read'????
'db file scattered read'
'db file parallel read'
'direct path read'???
'direct path write'??
'direct path read (lob)'
'direct path write (lob)'
與控制文件相關(guān)的IO事件
'control file parallel write'
'control file sequential read'
'control file single write'
與Redo日志相關(guān)的IO事件
'log file parallel write'????
'log file sync'?
'log file sequential read'
'log file single write'
'switch logfile command'
'log file switch completion'
'log file switch (clearing log file)'
'log file switch (checkpoint incomplete)'
'log switch/archive'
'log file switch (archiving needed)'
與Buffer Cache相關(guān)的IO事件
'db file parallel write'
'db file single write'
'write complete waits'
'free buffer waits'
下面我們就分別介紹如何解決IO問題。
3.1???? IO調(diào)優(yōu)的思路及常用手段
通過對statspack或者awr報告的分析,我們可以得知是那些IO相關(guān)事件引起的IO問題。針對不同的事件,可以采取不同的分析、處理方法。而有一些通用的方法并不是針對特定的事件的。我們這里先介紹一下這些方法。
3.1.1 通過SQL調(diào)優(yōu)來減少IO請求
一個沒有任何用戶SQL的數(shù)據(jù)庫幾乎不產(chǎn)生任何IO?;旧蠑?shù)據(jù)庫所有的IO都是直接或間接由用戶提交的SQL所導(dǎo)致的。這意味著我們可以通過控制單個SQL產(chǎn)生的IO來降低數(shù)據(jù)庫總的IO請求。而通過SQL調(diào)優(yōu)來降低SQL查詢計劃中的IO操作次數(shù)則是降低SQL產(chǎn)生IO的最好方法。數(shù)據(jù)庫的性能問題通常是由少數(shù)幾個SQL語句所導(dǎo)致的,它們產(chǎn)生了大量IO導(dǎo)致了整個數(shù)據(jù)庫的性能下降。優(yōu)化幾條問題語句往往就能解決整個數(shù)據(jù)庫的IO性能問題。
從Oracle 10g開始,ADDM能夠自動檢測出問題語句,同時,再通過查詢優(yōu)化建議器能夠自動優(yōu)化語句并降低它們對IO的消耗。關(guān)于ADDM和查詢優(yōu)化建議器可以參考文章《Oracle 10G 新特性——ADDM和查詢優(yōu)化建議器》。
3.1.2 通過調(diào)整實例參數(shù)來減少IO請求
在這種方法中,主要有兩種途徑來實現(xiàn)對IO的優(yōu)化。
使用內(nèi)存緩存來減少IO
通過一些內(nèi)存緩存,如Buffer Cache、Log Buffer、Sort Area,可以降低數(shù)據(jù)庫對IO的請求。
當(dāng)Buffer Cache被增大到一定大小時,絕大多數(shù)結(jié)果可以直接從緩存中獲取到,而無需從磁盤上讀取了。而在進(jìn)行排序操作時,如果Sort Area足夠大,排序過程中產(chǎn)生的臨時數(shù)據(jù)可以直接放在內(nèi)存中,而無需占用臨時表空間了。
調(diào)整multiblock IO(多數(shù)據(jù)塊IO)的大小
控制Multiblock IO的參數(shù)叫DB_FILE_MULTIBLOCK_READ_COUNT,它控制在多數(shù)據(jù)塊讀時一次讀入數(shù)據(jù)塊的次數(shù)。適當(dāng)增加這個參數(shù)大小,能夠提高多數(shù)據(jù)塊操作(如全表掃描)的IO效率。例如,讀取100M數(shù)據(jù),如果每次讀取1M一共讀取100次的效率就比每次讀取100K一共讀取1000次更快。但是這個數(shù)字達(dá)到一定大小后,再增加就作用不大了:每次10M一共讀100次來讀取1G的數(shù)據(jù)的效率和單獨一次讀取1G數(shù)據(jù)的效率是沒有多大區(qū)別的。這是因為IO效率受到2個因素的影響:IO建立時間和IO傳輸時間。
IO建立時間對于不同IO大小來說都是相同的,它決定了對小IO的總的IO時間,增大Multiblock IO大小可以減少IO建立時間;
IO傳輸時間與IO大小是成正比的,在小IO時,IO傳輸時間一般比IO建立時間少,但對于大IO操作來說,IO傳輸時間決定了總的IO時間。因此Multiblock IO大小增大到一定大小時,它對總的IO時間影響就不大了。
3.1.3 在操作系統(tǒng)層面優(yōu)化IO
如我們前面所介紹的,利用一些操作系統(tǒng)提供的提升IO性能的特性,如文件系統(tǒng)的異步IO、Direct IO等來優(yōu)化數(shù)據(jù)庫系統(tǒng)的IO性能。另外一種方法就是增加每次傳輸?shù)淖畲驣O大小的限制(大多數(shù)Unix系統(tǒng)中,由參數(shù)max_io_size控制)。
3.1.4 通過Oracle ASM實現(xiàn)對IO的負(fù)載均衡
ASM(Automatic Storage Manager自動存儲管理)是從Oracle 10g開始引入的。它是一個建立在數(shù)據(jù)庫內(nèi)核中的文件系統(tǒng)和卷管理器。它能自動將IO負(fù)載均衡到所有可用的磁盤啟動器上去,一避免“熱區(qū)”。ASM能防止碎片,因此無需重建數(shù)據(jù)來回收空間。數(shù)據(jù)被均衡分布到所有硬盤上。
3.1.5 通過條帶化、RAID、SAN或者NAS實現(xiàn)對IO的負(fù)載均衡
這個方法通過一些成熟的存儲技術(shù),如條帶化、RAID、SAN和NAS,來將數(shù)據(jù)庫IO分布到多個可用的物理磁盤實現(xiàn)負(fù)載均衡,以避免在還存在空閑可用磁盤時出現(xiàn)的磁盤爭用和IO瓶頸問題。
關(guān)于這幾種存儲技術(shù),我們文章的前面部分都有做介紹。
3.1.6 通過手工布置數(shù)據(jù)庫文件到不同的文件系統(tǒng)、控制器和物理設(shè)備上來重新分布數(shù)據(jù)庫IO
當(dāng)數(shù)據(jù)庫系統(tǒng)中缺乏以上各種存儲技術(shù)手段時,我們可以考慮使用這種方式。這樣做的目的是使數(shù)據(jù)庫的IO得到均勻分布,從而避免在還有空閑磁盤時出現(xiàn)磁盤爭用和IO瓶頸問題。當(dāng)然這種手工分布IO方法是無法達(dá)到以上的自動分布IO的效果的。
3.1.7 其他手段
系統(tǒng)中總會存在一些IO是無法消除或降低的。如果采用以上手段還不能滿足IO性能要求的話,可以考慮這兩種方法:
將老數(shù)據(jù)移除你的生產(chǎn)數(shù)據(jù)庫(Housekeep)采用更多、更快的硬件3.2???? 數(shù)據(jù)文件相關(guān)的IO事件
數(shù)據(jù)庫系統(tǒng)中的大多數(shù)的IO請求都是針對數(shù)據(jù)文件的。因此大多數(shù)情況下,與數(shù)據(jù)文件相關(guān)的IO事件是引起系統(tǒng)IO性能的主要原因。