oracle tuning

dragon路發表於2011-07-31
  關於Oracle的效能調整,一般包括兩個方面,一是指Oracle資料庫本身的調整,比如SGA、PGA的優化設定,二是連線Oracle的應用程式以及SQL語句的優化。做好這兩個方面的優化,就可以使一套完整的Oracle應用系統處於良好的執行狀態。
        本文主要是把一些Oracle Tuning的文章作了一個簡單的總結,力求以實際可操作為目的,配合講解部分理論知識,使大部分具有一般Oracle知識的使用者能夠對Oracle Tuning有所瞭解,並且能夠根據實際情況對某些引數進行調整。關於更加詳細的知識,請參見本文結束部分所提及的推薦書籍,同時由於該話題內容太多且複雜,本文必定有失之偏頗甚至錯誤的地方,請不吝賜教,並共同進步。

1.  SGA的設定
        在Oracle Tuning中,對SGA的設定是關鍵。SGA,是指Shared Global Area , 或者是 System Global Area , 稱為共享全域性區或者系統全域性區,結構如下圖所示。


 
        對於SGA區域內的記憶體來說,是共享的、全域性的,在UNIX 上,必須為oracle 設定共享記憶體段(可以是一個或者多個),因為oracle 在UNIX上是多程式;而在WINDOWS上oracle是單程式(多個執行緒),所以不用設定共享記憶體段。

1.1  SGA的各個組成部分
下面用 sqlplus 查詢舉例看一下 SGA 各個組成部分的情況:
SQL> select * from v$sga;
NAME                      VALUE
--------------------              ----------
Fixed Size                   104936
Variable Size              823164928
Database Buffers          1073741824
Redo Buffers                 172032

或者
SQL> show sga
Total System Global Area   1897183720 bytes
Fixed Size                   104936 bytes
Variable Size              823164928 bytes
Database Buffers          1073741824 bytes
Redo Buffers                 172032 bytes

Fixed Size
        oracle 的不同平臺和不同版本下可能不一樣,但對於確定環境是一個固定的值,裡面儲存了SGA 各部分元件的資訊,可以看作引導建立SGA的區域。

Variable Size
        包含了shared_pool_size、java_pool_size、large_pool_size 等記憶體設定

Database Buffers
        指資料緩衝區,在8i 中包含db_block_buffer*db_block_size、buffer_pool_keep、buffer_pool_recycle 三部分記憶體。在9i 中包含db_cache_size、db_keep_cache_size、db_recycle_cache_size、 db_nk_cache_size。

Redo Buffers
       指日誌緩衝區,log_buffer。在這裡要額外說明一點的是,對於v$parameter、v$sgastat、v$sga查詢值可能不一樣。v$ parameter 裡面的值,是指使用者在初始化引數檔案裡面設定的值,v$sgastat是oracle 實際分配的日誌緩衝區大小(因為緩衝區的分配值實際上是離散的,也不是以block 為最小單位進行分配的),v$sga 裡面查詢的值,是在oracle 分配了日誌緩衝區後,為了保護日誌緩衝區,設定了一些保護頁,通常我們會發現保護頁大小是8k(不同環境可能不一樣)。參考如下內容
SQL> select substr(name,1,10) name,substr(value,1,10) value
       2 from v$parameter where name = 'log_buffer';
NAME                 VALUE
--------------------  --------------------
log_buffer              163840

SQL> select * from v$sgastat where pool is null;

POOL        NAME                       BYTES
----------- --------------------------                ----------
            fixed_sga                      104936
            db_block_buffers            1073741824
            log_buffer                     163840

SQL> select * from v$sga;

NAME                      VALUE
--------------------              ----------
Fixed Size                   104936
Variable Size               823164928
Database Buffers           1073741824
Redo Buffers                 172032

172032 – 163840 = 8192

(以上試驗資料是在 HP B.11.11 + Oracle 8.1.7.4 環境下得到的)


1.2  SGA的大小設定
       在對SGA的結構進行簡單分析以後,下面是關於如何根據系統的情況正確設定SGA大小的問題。
SGA是一塊記憶體區域,佔用的是系統實體記憶體,因此對於一個Oracle應用系統來說,SGA決不是越大越好,這就需要尋找一個系統優化的平衡點。


1.2.1  設定引數前的準備
在設定SGA的記憶體引數之前,我們首先要問自己幾個問題
一:實體記憶體多大
二:作業系統估計需要使用多少記憶體
三:資料庫是使用檔案系統還是裸裝置
四:有多少併發連線
五:應用是OLTP 型別還是OLAP 型別

根據這幾個問題的答案,我們可以粗略地為系統估計一下記憶體設定。那我們現在來逐個問題地討論,首先實體記憶體多大是最容易回答的一個問題,然後作業系統估計使用多少記憶體呢?從經驗上看,不會太多,通常應該在200M 以內(不包含大量程式PCB)。
接下來我們要探討一個重要的問題,那就是關於檔案系統和裸裝置的問題,這往往容易被我們所忽略。作業系統對於檔案系統,使用了大量的buffer 來快取作業系統塊。這樣當資料庫獲取資料塊的時候,雖然SGA 中沒有命中,但卻實際上可能是從作業系統的檔案快取中獲取的。而假如資料庫和作業系統支援非同步IO,則實際上當資料庫寫程式DBWR寫磁碟時,作業系統在檔案快取中標記該塊為延遲寫,等到真正地寫入磁碟之後,作業系統才通知DBWR寫磁碟完成。對於這部分檔案快取,所需要的記憶體可能比較大,作為保守的估計,我們應該考慮在 0.2——0.3 倍記憶體大小。但是如果我們使用的是裸裝置,則不考慮這部分快取的問題。這樣的情況下SGA就有調大的機會。
關於資料庫有多少併發連線,這實際上關係到PGA 的大小(MTS 下還有large_pool_size)。事實上這個問題應該說還跟OLTP 型別或者OLAP 型別相關。對於OLTP型別oracle 傾向於可使用MTS,對於OLAP 型別使用獨立模式,同時OLAP 還可能涉及到大量的排序操作的查詢,這些都影響到我們記憶體的使用。那麼所有的問題綜合起來,實際上主要反映在UGA的大小上。UGA主要包含以下部分記憶體設定
SQL> show parameters area_size

NAME                                 TYPE    VALUE
------------------------------------               -------     --------
bitmap_merge_area_size                   integer    1048576
create_bitmap_area_size                   integer    8388608
hash_area_size                           integer     131072
sort_area_size                            integer     65536
SQL>

在這部分記憶體中我們最關注的通常是sort_area_size,這是當查詢需要排序的時候,資料庫會話將使用這部分記憶體進行排序,當記憶體大小不足的時候,使用臨時表空間進行磁碟排序。由於磁碟排序效率和記憶體排序效率相差好幾個數量級,所以這個引數的設定很重要。
當出現大量排序時的磁碟I/O操作時,可以考慮增加sort_area_size的值。sort_area_size是Oracle用於一次排序所需的最大記憶體數,在排序結束但是結果列返回之前,Oracle會釋放sort_area_size大小的記憶體,但是會保留 sort_area_retained_size大小的記憶體,知道最後一行結果列返回以後,才釋放所有的記憶體。
會導致排序的語句有 SELECT DISTINCT , MINUS , INTERSECT , UNION 和 min()、max()、count() 操作;而不會導致排序的語句有 UPDATE , 帶BETWEEN子句的SELECT 等等。
這四個引數都是針對會話進行設定的,是單個會話使用的記憶體的大小,而不是整個資料庫使用的。偶爾會看見有人誤解了這個引數以為是整個資料庫使用的大小,這是極其嚴重的錯誤。假如設定了MTS,則UGA被分配在large_pool_size,也就是說放在了共享記憶體裡面,不同程式(執行緒)之間可以共享這部分記憶體。在這個基礎上,我們假設資料庫存在併發執行server process 為100 個,根據上面我們4 個引數在oracle8.1.7 下的預設值,我們來計算獨立模式下PGA 的大致大小。由於會話並不會經常使用create_bitmap_area_size 、bitmap_merge_area_size,所以我們通常不對四個引數求和。在考慮到除這四個引數外會話所儲存的變數、堆疊等資訊,我們估計為 2M,則200 個程式最大可能使用200M 的PGA。

1.2.2  一個經驗公式
       現在,根據上面這些假定,我們來看SGA 實際能達到多少記憶體。在1G 的記憶體的伺服器上,我們能分配給SGA 的記憶體大約為400—500M。若是2G 的記憶體,大約可以分到1G的記憶體給SGA,8G 的記憶體可以分到5G的記憶體給SGA。當然我們這裡是以預設的排序部分記憶體sort_area_size=64k進行衡量的,假如我們需要調大該引數和 hash_area_size等引數,然後我們應該根據併發的程式的數量,來衡量考慮這個問題。

事實上,通常我們更習慣通過直觀的公式化來表達這樣的問題:
OS 使用記憶體+SGA+併發執行程式數*(sort_area_size+hash_ara_size+2M) < 0.7*總記憶體

(公式是死的,系統是活的,實際應用的調整不必框公式,這不過是一個參考建議)

在我們的實際應用中,假如採用的是裸裝置,我們可適當的增大SGA(如果需要的話)。由於目前幾乎所有的作業系統都使用虛擬快取,所以實際上如果就算SGA 設定的比較大也不會導致錯誤,而是可能出現頻繁的記憶體頁的換入與換出(page in/out)。在作業系統一級如果觀察到這個現象,那麼我們就需要調整記憶體的設定。


1.2.3  各個引數的設定
那麼SGA中的各個引數具體應該按照什麼樣的原則來設定呢,下面進行討論:
log_buffer
對於日誌緩衝區的大小設定,通常我覺得沒有過多的建議,因為參考LGWR寫的觸發條件之後,我們會發現通常超過3M意義不是很大。作為一個正式系統,可能考慮先設定這部分為log_buffer=1—3M 大小,然後針對具體情況再調整。
large_pool_size
對於大緩衝池的設定,假如不使用MTS,建議在20—30M 足夠了。這部分主要用來儲存並行查詢時候的一些資訊,還有就是RMAN 在備份的時候可能會使用到。如果設定了MTS,則由於UGA部分要移入這裡,則需要具體根據session最大數量和 sort_ares_size 等相關會話記憶體引數的設定來綜合考慮這部分大小的設定,一般可以考慮為 session * (sort_area_size + 2M)。這裡要提醒一點,不是必須使用MTS,我們都不主張使用MTS,尤其同時線上使用者數小於500的情況下。。
java_pool_size
假如資料庫沒有使用JAVA,我們通常認為保留10—20M大小足夠了。事實上可以更少,甚至最少只需要32k,但具體跟安裝資料庫的時候的元件相關(比如http server)。
shared_pool_size
這是迄今為止最具有爭議的一部分記憶體設定。按照很多文件的描述,這部分內容應該幾乎和資料緩衝區差不多大小。但實際上情況卻不是這樣的。首先我們要考究一個問題,那就是這部分記憶體的作用,它是為了快取已經被解析過的SQL,而使其能被重用,不再解析。這樣做的原因是因為,對於一個新的SQL (shared_pool 裡面不存在已經解析的可用的相同的SQL),資料庫將執行硬解析,這是一個很消耗資源的過程。而若已經存在,則進行的僅僅是軟分析(在共享池中尋找相同 SQL),這樣消耗的資源大大減少。所以我們期望能多共享一些SQL,並且如果該引數設定不夠大,經常會出現ora-04031錯誤,表示為了解析新的 SQL,沒有可用的足夠大的連續空閒空間,這樣自然我們期望該引數能大一些。但是該引數的增大,卻也有負面的影響,因為需要維護共享的結構,記憶體的增大也會使得SQL 的老化的代價更高,帶來大量的管理的開銷,所有這些可能會導致CPU 的嚴重問題。

在一個充分使用繫結變數的比較大的系統中,shared_pool_size 的開銷通常應該維持在300M 以內。除非系統使用了大量的儲存過程、函式、包,比如oracle erp 這樣的應用,可能會達到500M甚至更高。於是我們假定一個1G記憶體的系統,可能考慮設定該引數為100M,2G 的系統考慮設定為150M,8G 的系統可以考慮設定為200—300M。
對於一個沒有充分使用或者沒有使用繫結變數系統,這可能給我們帶來一個嚴重的問題。所謂沒有使用bind var 的SQL,我們稱為Literal SQL。也就是比如這樣的兩句SQL我們認為是不同的SQL,需要進行2 次硬解析:
select * from EMP where name = ‘TOM’;
select * from EMP where name = ‘JERRY’;
假如把 ’TOM’ 和 ’JERRY’ 換做變數V,那就是使用了bind var,我們可以認為是同樣的SQL 從而能很好地共享。共享SQL 本來就是shared_pool_size 這部分記憶體存在的本意,oracle的目的也在於此,而我們不使用bind var 就是違背了oracle 的初衷,這樣將給我們的系統帶來嚴重的問題。當然,如果通過在作業系統監控,沒有發現嚴重的cpu問題,我們如果發現該共享池命中率不高可以適當的增加 shred_pool_size。但是通常我們不主張這部分記憶體超過800M(特殊情況下可以更大)。
事實上,可能的話我們甚至要想辦法避免軟分析,這在不同的程式語言中實現方式有差異。我們也可能通過設定session_cached_cursors 引數來獲得幫助(這將增大PGA)
關於使用繫結變數的話題,在下面的應用優化中繼續討論。

Data buffer
現在我們來談資料緩衝區,在確定了SGA 的大小並分配完了前面部分的記憶體後,其餘的,都分配給這部分記憶體。通常,在允許的情況下,我們都嘗試使得這部分記憶體更大。這部分記憶體的作用主要是快取 DB BLOCK,減少甚至避免從磁碟上獲取資料,在8i中通常是由db_block_buffers*db_block_size 來決定大小的。如果我們設定了buffer_pool_keep 和buffer_pool_recycle,則應該加上後面這兩部分記憶體的大小。

可以看出,設定SGA時基本上應該掌握的原則是:
  data buffer 一般可以儘可能的大
  shared_pool_size 應該適度
  log buffer 在 1MB 以內就可以了

假定oracle是 32 bit ,伺服器RAM大於2G ,注意你的PGA的情況,,則建議
shared_pool_size + data buffer +large_pool_size + java_pool_size < 1.6G

再具體化,如果512M RAM
建議 shared_pool_size = 50M, data buffer = 200M

如果1G RAM
shared_pool_size = 100M , data buffer = 500M

如果2G RAM
shared_pool_size = 150M ,data buffer = 1.2G

實體記憶體再大已經跟引數沒有關係了

假定64 bit ORACLE
記憶體4G
shared_pool_size = 200M , data buffer = 2.5G

記憶體8G
shared_pool_size = 300M , data buffer = 5G

記憶體 12G
shared_pool_size = 300M-----800M , data buffer = 8G

1.3  32bit 與 64bit 對SGA的影響
為什麼在上面SGA大小設定的經驗規則中要分 32bit Oracle 和 64bit Oracle 呢,是因為這關係到SGA大小的上限問題。在32bit的資料庫下,通常oracle只能使用不超過1.7G的記憶體,即使我們擁有12G的記憶體,但是我們卻只能使用1.7G,這是一個莫大的遺憾。假如我們安裝64bit的資料庫,我們就可以使用很大的記憶體,幾乎不可能達到上限。但是64bit 的資料庫必須安裝在64bit 的作業系統上,可惜目前windows上只能安裝32bit的資料庫,我們通過下面的方式可以檢視資料庫是 32bit 還是 64bit :
SQL> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
PL/SQL Release 8.1.7.0.0 - Production
CORE 8.1.7.0.0 Production
TNS for 32-bit Windows: Version 8.1.7.0.0 - Production
NLSRTL Version 3.4.1.0.0 – Production

在UNIX平臺下的顯示有所不同,明顯可以看出是 64bit Oracle ,比如在HP-UX平臺上:
SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle8i Enterprise Edition Release 8.1.7.4.0 - 64bit Production
PL/SQL Release 8.1.7.4.0 - Production
CORE    8.1.7.0.0       Production
TNS for HPUX: Version 8.1.7.4.0 - Production
NLSRTL Version 3.4.1.0.0 – Production

32bit的oracle無論跑在32bit或者64bit的平臺都有SGA的限制的,而對於32bit的平臺只能跑32bit的oracle,但是在特定的作業系統下,可能提供了一定的手段,使得我們可以使用超過1.7G 的記憶體,達到2G 以上甚至更多。由於我們現在一般都使用64bit Oracle,因此關於如何在32bit平臺上擴充套件SGA大小的問題不再贅述。


1.4  9i中相關引數的變化
oracle的版本的更新,總是伴隨著引數的變化,並且越來越趨向於使得引數的設定更簡單,因為複雜的引數設定使得DBA們經常焦頭爛額。關於記憶體這部分的變化,我們可以考察下面的引數。事實上在9i中資料庫本身可以給出一組適合當前執行系統的SGA相關部分的引數調整值(參考V$ DB_CACHE_ADVICE、V$SHARED_POOL_ADVICE),關於PGA也有相關檢視V$PGA_TARGET_ADVICE 等。

Data buffer
9i 中保留了8i中的引數,如設定了新的引數,則忽略舊的引數。9i中用db_cache_size來取代db_block_buffers , 用db_keep_cache_size 取代buffer_pool_keep, 用db_recycle_cache_size 取代buffer_pool_recycle;這裡要注意9i 中設定的是實際的快取大小而不再是塊的數量。另外9i新增加了db_nk_cache_size,這是為了支援在同一個資料庫中使用不同的塊大小而設定的。對於不同的表空間,可以定義不同的資料塊的大小,而緩衝區的定義則依靠該引數的支援。其中n 可以為2、4、6、8、16 等不同的值。在這裡順便提及的一個引數就是db_block_lru_latches,該引數在9i中已經成為了保留引數,不推薦手工設定。

PGA
在9i 裡面這部分也有了很大的變化。在獨立模式下,9i已經不再主張使用原來的UGA相關的引數設定,而代之以新的引數。假如 workarea_size_policy=AUTO(預設),則所有的會話的UGA 共用一大塊記憶體,該記憶體由 pga_aggregate_target 設定。在我們根據前面介紹的方法評估了所有程式可能使用的最大PGA 記憶體之後,我們可以通過在初始化引數中設定這個引數,從而不再關心其他 ”*_area_size” 引數。

SGA_MAX_SIZE
在9i中若設定了SGA_MAX_SIZE,則在總和小於等於這個值內,可以動態的調整資料緩衝區和共享池的大小
SQL> show parameters sga_max_size
NAME       TYPE             VALUE
---------------- -------------------- ------- -------------
sga_max_size  unknown         193752940
SQL>
SQL> alter system set db_cache_size = 30000000;
System altered.
SQL> alter system set shared_pool_size = 20480000;
System altered.


1.5  lock_sga = true 的問題
由於幾乎所有的作業系統都支援虛擬記憶體,所以即使我們使用的記憶體小於實體記憶體,也不能避免作業系統將SGA 換到虛擬記憶體(SWAP)。所以我們可以嘗試使得SGA 鎖定在實體記憶體中不被換到虛擬記憶體中,這樣減少頁面的換入和換出,從而提高效能。但在這裡遺憾的是,windows 是無法避免這種情況的。下面我們來參考在不同的幾個系統下怎麼實現lock_sga
AIX 5L(AIX 4.3.3 以上)
logon aix as root
cd /usr/samples/kernel
./vmtune (資訊如下) v_pingshm已經是1
./vmtune -S 1
然後oracle使用者修改initSID.ora 中 lock_sga = true
重新啟動資料庫

HP UNIX
Root身份登陸
Create the file "/etc/privgroup": vi /etc/privgroup
Add line "dba MLOCK" to file
As root, run the command "/etc/setprivgrp -f /etc/privgroup":
$/etc/setprivgrp -f /etc/privgroup
oracle使用者修改initSID.ora中lock_sga=true
重新啟動資料庫

SOLARIS (solaris2.6以上)
8i版本以上資料庫預設使用隱藏引數 use_ism = true ,自動鎖定SGA於記憶體中,不用設定lock_sga, 如果設定 lock_sga =true 使用非 root 使用者啟動資料庫將返回錯誤。

WINDOWS
不能設定lock_sga=true,可以通過設定pre_page_sga=true,使得資料庫啟動的時候就把所有記憶體頁裝載,這樣可能起到一定的作用。

 


2.  應用優化
下面我們從技術的角度入手,來探討資料庫優化方面的問題。通常作為優化Oracle系統的人,或者是DBA,其實很多時候對應用並不很瞭解甚至可以說是完全不瞭解,更不要說對應用程式程式碼的瞭解。事實上呢,一個系統執行的快或者慢相信大家都明白,第一重要的是資料庫的設計,然後是應用的設計, SQL語句的編寫,最後才是資料庫引數的調整和硬體、網路的問題,等等。所以在我們不瞭解一個系統的時候來優化資料庫應用不是一個輕鬆的容易的事情。那麼我們第一步應該怎麼做呢?
通常有兩類方法:
其中一個方法就是我們常用的,使用statspack來進行診斷系統的瓶頸所在。在statspack中oracle給出了幾乎涵蓋oracle大部分重要內容的資訊。
另外一種方式,就是trace session。假如某個session執行很慢或者某個使用者的某個查詢很慢,那麼這個時候我們可以通過trace session的方式來診斷到底是慢在哪裡,看究竟執行計劃是怎樣的,然後在user_dump_dest下根據該session的程式號或者執行緒號可以找到一個產生的trace檔案。通過使用tkprof格式化檔案之後我們就可以看見很多的統計資訊,這裡包括了執行計劃、parse/fetch等步驟消耗cpu的時間。通常我們是觀察query模式下的consistent gets來首先看sql是否使用了索引,然後看執行計劃是不是正常,是不是有調整的餘地。當然如果您沒有實際做過的話,這些內容說起來很抽象。這是在不瞭解應用和程式下針對特定session的診斷和調整過程。
trace session的方式是一種自下而上的方法,從sql入手;而statspack是自頂向下的方法,也就是從巨集觀上先診斷資料庫的瓶頸在哪裡,然後從瓶頸入手來做調整,這個習慣上又可以稱為通過等待事件(wait event)入手的方法。

2.1  使用statspack
statspack是一個效能診斷工具,首先釋出於Oracle8.1.6版本,在8.1.7版本中功能得到加強。Statspack除了查詢例項中的效能問題外,還可以查詢應用程式中高負荷的SQL語句,很容易確定Oracle 資料庫的瓶頸所在,並且記錄資料庫效能狀態。
資料庫中Statspack 的指令碼位於$ORACLE_HOME/RDBMS/ADMIN 目錄下,對於ORACLE8.1.6,是一組以stat 開頭的檔案;對於ORACLE8.1.7,是一組以sp 開頭的檔案。
在Statspack 釋出之前,我們通常能夠使用診斷資料庫的工具是兩個指令碼UTLBSTAT.SQL 和UTLESTAT.SQL,BSTAT/ESTAT 是一個非常簡單的效能診斷工具。UTLBSTAT 獲得開始時很多V$檢視的快照,UTLESTAT 通過先前的快照和當前檢視生成一個報表。
該報表實際上相當於statspack 中的兩個取樣點。
Statspack 通過連續的取樣,能夠給我們提供至關重要的趨勢分析資料。這是一個巨大的進步。能夠使用Statspack 的環境我們就儘量不要使用BSTAT/ESTAT 的方式來診斷資料庫問題。

2.1.1  安裝statapack
§ 步驟一:
為了能夠順利安裝和執行Statspack ,首先需要設定以下兩個系統引數:
1.  job_queue_processes
為了能夠建立自動任務,執行資料收集,該引數需要大於0。你可以在初試化引數檔案中修改該引數(使該引數在重起後以然有效)。
該引數可以在系統級動態修改(重起後失效)。

SQL> alter system set job_queue_processes = 6;
System altered

在Oracle9i 當中,可以指定範圍,如 both,這樣該修改在當前及之後保持有效(僅當你使用spfile 時,如果在9i 中仍然使用pfile,那麼更改方法同8i 相同):

SQL> alter system set job_queue_processes = 6 scope=both;
System altered


2.  timed_statistics
收集作業系統的計時資訊,這些資訊可被用來顯示時間等統計資訊、優化資料庫和 SQL 語句。要防止因從作業系統請求時間而引起的開銷,請將該值設定為False。
使用statspack 收集統計資訊時建議將該值設定為 TRUE,否則收集的統計資訊大約只能起到10%的作用,將timed_statistics 設定為True 所帶來的效能影響與好處相比是微不足道的。
該引數使收集的時間資訊儲存在在V$SESSTATS 和V$SYSSTATS 等動態效能檢視中。
timed_statistics 引數也可以在例項級進行更改

SQL> alter system set timed_statistics = true;
System altered

如果你擔心一直啟用timed_statistics 對於效能的影響,你可以在使用statspack 之前在system 更改,取樣過後把該引數動態修改成false。

§ 步驟二:
需要單獨為statspack建立一個儲存資料的表空間,如果取樣間隔較短,週期較長,打算長期使用,那麼可能需要一個大一點的表空間,如果每個半個小時取樣一次,連續取樣一週,資料量是很大的。下面的例子中建立了一個500M 的測試表空間。
注意: 這裡建立的表空間不能太小,如果太小的話建立物件會失敗,建議至少建立100M 表空間。

SQL> create tablespace perfstat
2 datafile '/oracle/oradata/oradata/res/perfstat.dbf'
3 size 500M;
Tablespace created。

§ 步驟三:
在 sqlplus 中用internal 身份登陸,或者擁有SYSDBA(connect / as sysdba)許可權的使用者登陸。
注: 在Oracle9i 中,不存在internal 使用者,可以使用sys 使用者以sysdba 身份連線。
先轉到$ORACLE_HOME/RDBMS/ADMIN 目錄,檢查安裝指令碼是否存在,同時我們執行指令碼也可以方便些。

$ cd $ORACLE_HOME/rdbms/admin
$ ls -l sp*.sql
-rw-r--r--   1 oracle   other       1774 Feb 18  2000 spauto.sql
-rw-r--r--   1 oracle   other      62545 Jun 15  2000 spcpkg.sql
-rw-r--r--   1 oracle   other        877 Feb 18  2000 spcreate.sql
-rw-r--r--   1 oracle   other      31193 Jun 15  2000 spctab.sql
-rw-r--r--   1 oracle   other       6414 Jun 15  2000 spcusr.sql
-rw-r--r--   1 oracle   other        758 Jun 15  2000 spdrop.sql
-rw-r--r--   1 oracle   other       3615 Jun 15  2000 spdtab.sql
-rw-r--r--   1 oracle   other       1274 Jun 15  2000 spdusr.sql
-rw-r--r--   1 oracle   other       6760 Jun 15  2000 sppurge.sql
-rw-r--r--   1 oracle   other      71034 Jul 12  2000 spreport.sql
-rw-r--r--   1 oracle   other       2191 Jun 15  2000 sptrunc.sql
-rw-r--r--   1 oracle   other      30133 Jun 15  2000 spup816.sql
$

接下來我們就可以開始安裝Statspack 了。在Oracle8.1.6 版本中執行statscre.sql; 在Oracle8.1.7 版本中執行spcreate.sql。
這期間會提示你輸入預設表空間和臨時表空間的位置,輸入我們為 perfstat 使用者建立的表空間和你的臨時表空間。安裝指令碼會自動建立perfstat 使用者。

$ sqlplus

SQL*Plus: Release 8.1.7.0.0 - Production on Sat Jul 26 16:27:31 2003

(c) Copyright 2000 Oracle Corporation.  All rights reserved.

Enter user-name: internal

Connected to:
Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
With the Partitioning option
JServer Release 8.1.7.0.0 - Production

SQL>
SQL> @spcreate
... Installing Required Packages

Package created.

Grant succeeded.

View created.

Package body created.

Package created.

Synonym dropped.

Synonym created.
……

Specify PERFSTAT user's default   tablespace
Enter value for default_tablespace: perfstat
Using perfstat for the default tablespace

User altered.

User altered.

Specify PERFSTAT user's temporary tablespace
Enter value for temporary_tablespace: temp
Using temp for the temporary tablespace

User altered.

NOTE:
SPCUSR complete. Please check spcusr.lis for any errors.

……

如果安裝成功,你可以接著看到如下的輸出資訊:
….
Creating Package STATSPACK...

Package created.

No errors.
Creating Package Body STATSPACK...

Package body created.

No errors.

NOTE:
SPCPKG complete. Please check spcpkg.lis for any errors.

可以檢視.lis 檔案檢視安裝時的錯誤資訊。

§ 步驟四:
如果安裝過程中出現錯誤,那麼可以執行spdrop.sql 指令碼來刪除這些安裝指令碼建立的物件。然後重新執行spcreate.sql來建立這些物件。

SQL> @spdrop
Dropping old versions (if any)

Synonym dropped.

Sequence dropped.

Synonym dropped.

Table dropped.

Synonym dropped.

View dropped.
……
NOTE:
SPDUSR complete. Please check spdusr.lis for any errors.

(以上的安裝過程描述是在 HP 11.11 + Oracle 8.1.7 平臺上得到的)

2.1.2  測試statspack
執行statspack.snap 可以產生系統快照,執行兩次,然後執行spreport.sql 就可以生成一個基於兩個時間點的報告。
如果一切正常,說明安裝成功。

SQL>execute statspack.snap
PL/SQL procedure successfully completed.
SQL>execute statspack.snap
PL/SQL procedure successfully completed.
SQL>@spreport.sql

可是有可能你會得到以下錯誤:

SQL> exec statspack.snap;
BEGIN statspack.snap; END;
*
ERROR at line 1:
ORA-01401: inserted value too large for column
ORA-06512: at "PERFSTAT.STATSPACK", line 978
ORA-06512: at "PERFSTAT.STATSPACK", line 1612
ORA-06512: at "PERFSTAT.STATSPACK", line 71
ORA-06512: at line 1

這是Oracle 的一個Bug,Bug 號1940915。
該Bug 自8.1.7.3 後修正。
這個問題只會出現在多位的字符集, 需要修改spcpkg.sql 指令碼,$ORACLE_HOME/rdbms/admin/spcpkg.sql,將"substr" 修改為 "substrb",然後重新執行該指令碼。
該指令碼錯誤部分:
select l_snap_id
, p_dbid
, p_instance_number
, substr(sql_text,1,31)
...........
substr 會將多位的字元, 當作一個byte.substrb 則會當作多個byte。在收集資料時, statpack 會將 top10 的 sql 前 31 個位元組 存入資料表中,若在SQL 的前31 個字有中文,就會出現此錯誤。
注意:執行 spcpkg.sql 也需要以 internal 使用者登入 sqlplus


2.1.3  生成statspack報告
呼叫spreport.sql 可以生成分析報告:
當呼叫spreprot.sql 時,系統首先會查詢快照列表,然後要求你選擇生成報告的開始快照ID(begin_snap)和結束快照ID(end_snap),生成一個報告.
為了生成一個report,我們至少需要兩次取樣:


SQL> @spreport 

   DB Id    DB Name      Inst Num Instance
-----------   ------------       -------- ------------
  2749170756 RES              1      res

Completed Snapshots

                           Snap                    Snap
Instance     DB Name         Id   Snap Started    Level Comment
------------ ------------ ----- ----------------- ----- ----------------------
res          RES              1 26 Jul 2003 16:36     5
                              2 26 Jul 2003 16:37     5
                              3 26 Jul 2003 17:03     5


Specify the Begin and End Snapshot Ids
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enter value for begin_snap:2
Begin Snapshot Id specified: 2

Enter value for end_snap: 3
End   Snapshot Id specified: 3


Specify the Report Name
~~~~~~~~~~~~~~~~~~~~~~~
The default report file name is sp_2_3.  To use this name,
press to continue, otherwise enter an alternative.
Enter value for report_name: rep0726.txt 

 ……

 End of Report


在執行 spreport.sql 生成 statspack 報告的過程中,會有三個地方提示使用者輸入:
1、 開始快照ID;
2、 結束快照ID;
3、 輸出報告檔案的檔名,預設的檔名是sp__
上面輸入的開始快照ID是2,開始快照ID是3,輸出報告檔案的檔名是rep0726.txt
成功執行一次 statspack.snap 就會產生一個 snapshot ,在生成 statspack 報告的時候就可以看到這個 snap id 和 snap 執行的時間。執行 statspack.snap ,就是上面所說的取樣,statspack 報告是分析兩個取樣點之間各種情況。

2.1.4  刪除歷史快照資料
前面講過,成功執行一次 statspack.snap 就會產生一個 snapshot ,這個 snapshot 的基本資訊是存放在 PERFSTAT.stats$snapshot 表中的,生成 statspack報告時會查詢該表的資料,供使用者選擇準備分析的 snapshot 。如果執行 statspack.snap 次數多了以後,該表的資料也會增加,歷史資料會影響正常執行的效果,因此需要定時清理一下歷史快照資料。
刪除stats$snapshot 資料表中的相應資料,其他表中的資料會相應的級連刪除:

SQL> select max(snap_id) from stats$snapshot;
MAX(SNAP_ID)
------------
166

SQL> delete from stats$snapshot where snap_id < = 166;
143 rows deleted

你可以更改snap_id 的範圍以保留你需要的資料。
在以上刪除過程中,你可以看到所有相關的表都被鎖定。
SQL> select a.object_id,a.oracle_username ,b.object_name
from v$locked_object a,dba_objects b
where a.object_id = b.object_id
/
OBJECT_ID ORACLE_USERNAME OBJECT_NAME
------------------------------------- --------------------------------------------------------------------------------
156 PERFSTAT SNAP$
39700 PERFSTAT STATS$LIBRARYCACHE
39706 PERFSTAT STATS$ROLLSTAT
39712 PERFSTAT STATS$SGA
39754 PERFSTAT STATS$PARAMETER
39745 PERFSTAT STATS$SQL_STATISTICS
39739 PERFSTAT STATS$SQL_SUMMARY
39736 PERFSTAT STATS$ENQUEUESTAT
39733 PERFSTAT STATS$WAITSTAT
39730 PERFSTAT STATS$BG_EVENT_SUMMARY
39724 PERFSTAT STATS$SYSTEM_EVENT
39718 PERFSTAT STATS$SYSSTAT
39715 PERFSTAT STATS$SGASTAT
39709 PERFSTAT STATS$ROWCACHE_SUMMARY
39703 PERFSTAT STATS$BUFFER_POOL_STATISTICS
39697 PERFSTAT STATS$LATCH_MISSES_SUMMARY
39679 PERFSTAT STATS$SNAPSHOT
39682 PERFSTAT STATS$FILESTATXS
39688 PERFSTAT STATS$LATCH
174 PERFSTAT JOB$
20 rows selected

Oracle 還提供了系統指令碼用於Truncate 這些統計資訊表,這個指令碼名字是: sptrunc.sql (8i、9i 都相同)
該指令碼主要內容如下,裡面看到的就是statspack 相關的所有系統表:
truncate table STATS$FILESTATXS;
truncate table STATS$LATCH;
truncate table STATS$LATCH_CHILDREN;
truncate table STATS$LATCH_MISSES_SUMMARY;
truncate table STATS$LATCH_PARENT;
truncate table STATS$LIBRARYCACHE;
truncate table STATS$BUFFER_POOL_STATISTICS;
truncate table STATS$ROLLSTAT;
truncate table STATS$ROWCACHE_SUMMARY;
truncate table STATS$SGA;
truncate table STATS$SGASTAT;
truncate table STATS$SYSSTAT;
truncate table STATS$SESSTAT;
truncate table STATS$SYSTEM_EVENT;
truncate table STATS$SESSION_EVENT;
truncate table STATS$BG_EVENT_SUMMARY;
truncate table STATS$WAITSTAT;
truncate table STATS$ENQUEUESTAT;
truncate table STATS$SQL_SUMMARY;
truncate table STATS$SQL_STATISTICS;
truncate table STATS$SQLTEXT;
truncate table STATS$PARAMETER;
delete from STATS$SNAPSHOT;
delete from STATS$DATABASE_INSTANCE;
commit;

2.1.5  一些重要指令碼
1.通過匯出儲存及共享資料
在診斷系統問題時,可能需要向專業人士提供原始資料,這時我們可以匯出Statspack 表資料,
其中我們可能用到:spuexp.par
其內容主要為:
file=spuexp.dmp log=spuexp.log compress=y grants=y indexes=y rows=y constraints=y wner=PERFSTAT consistent=y
我們可以匯出如下:
exp userid=perfstat/my_perfstat_password parfile=spuexp.par

2.刪除資料
spdrop.sql 在執行時主要呼叫兩個指令碼: spdtab.sql 、spdusr.sql
前者刪除表及同義詞等資料,後者刪除使用者

3.Oracle92 中新增加的指令碼
1) 用於升級statspack 物件的指令碼,這些指令碼需要以具有SYSDBA 許可權的使用者執行, 升級前請先
備份存在的Schema 資料:
spup90.sql: 用於升級9.0 版本的模式至9.2 版本。
spup817.sql: 如果從Statspack 8.1.7 升級,需要執行這個指令碼
spup816.sql: 從Statspack 8.1.6 升級,需要執行這個指令碼,然後執行spup817.sql
2) sprepsql.sql 用於根據給定的SQL Hash 值生成SQL 報告


2.1.6  調整statspack的收集門限
Statspack 有兩種型別的收集選項:

1.級別(level):控制收集資料的型別
Statspack 共有三種快照級別,預設值是5
a. level 0: 一般效能統計。包括等待事件、系統事件、系統統計、回滾段統計、行快取、SGA、會話、鎖、緩衝池統計等等。
b. level 5: 增加SQL 語句。除了包括level0 的所有內容,還包括SQL 語句的收集,收集結果記錄在stats$sql_summary 中。
c. level 10: 增加子鎖存統計。包括level5 的所有內容。並且還會將附加的子鎖存存入stats$lathc_children 中。在使用這個級別時需要慎重,建議在Oracle support 的指導下進行。
可以通過statspack 包修改預設的級別設定
SQL>execute statspack.snap(i_snap_level=>0,i_modify_parameter=>’true’);
通過這樣的設定,以後的收集級別都將是0 級。
如果你只是想本次改變收集級別,可以忽略i_modify_parameter 引數。
SQL>execute statspack.snap(i_snap_level=>10);

2.快照門限:設定收集的資料的閾值。
快照門限只應用於stats$sql_summary 表中獲取的SQL 語句。
因為每一個快照都會收集很多資料,每一行都代表獲取快照時資料庫中的一個SQL 語句,所以stats$sql_summary 很快就會成為Statspack 中最大的表。
門限儲存在stats$statspack_parameter 表中。讓我們了結一下各種門限:
a. executions_th 這是SQL 語句執行的數量(預設值是100)
b. disk_reads_tn 這是SQL 語句執行的磁碟讀入數量(預設值是1000)
c. parse_calls_th 這是SQL 語句執行的解析呼叫的數量(預設值是1000)
d. buffer_gets_th 這是SQL 語句執行的緩衝區獲取的數量(預設值是10000)
任何一個門限值超過以上引數就會產生一條記錄。
通過呼叫statspack.modify_statspack_parameter 函式我們可以改變門限的預設值。
例如:
SQL>execute statspack.modify_statspack_parameter(i_buffer_gets_th=>100000,i_disk_reads_th=>100000;

2.2  對statspack報告的分析
從上面的描述可以看出,產生一個statspack報告是比較簡單的,但是如何讀懂statspack報告卻不是那麼容易,需要對Oracle的體系架構、記憶體結構、等待事件以及應用系統有充分的瞭解,加上不斷的實踐,才能基本讀懂statspack報告並且從報告中找到調整優化Oracle的途徑。
下面接合一個實際的statspack報告,大致分析一下。

2.2.1  基本資訊分析
DB Name         DB Id    Instance     Inst Num Release     OPS Host
------------ ----------- ------------ --------          ----------- ---      ---------  ---
RES           2749170756 res                 1  8.1.7.0.0   NO  res

                Snap Id     Snap Time      Sessions
                ------- ------------------ --------
 Begin Snap:          2 26-Jul-03 16:37:08       38
   End Snap:          3 26-Jul-03 17:03:23       38
    Elapsed:                  26.25 (mins)

Statspack報告首先描述了資料庫的基本情況,比如資料庫名、例項名、例項個數、oracle版本號等等;然後是該報告的開始快照和結束快照的資訊,包括 snap id , snap time 等等;最後是該報告經過的時間跨度,單位是分鐘(mins)。

Cache Sizes
~~~~~~~~~~~
db_block_buffers:      61440          log_buffer:     163840
db_block_size:         8192     shared_pool_size:   52428800

然後描述了Oracle記憶體結構中幾個重要的引數。

2.2.2  記憶體資訊分析
Load Profile
~~~~~~~~~~~~                       Per Second       Per Transaction
                                   ---------------       ---------------
              Redo size:              4,834.87             11,116.67
          Logical reads:                405.53                932.43
          Block changes:                 60.03                138.02
          Physical reads:                138.63                318.75
          Physical writes:                 54.27                124.79
          User calls:                     62.69                144.13
          Parses:                        19.14                 44.00
          Hard parses:                    2.26                  5.20
                  Sorts:                  1.83                  4.20
                 Logons:                  0.21                  0.47
               Executes:                 21.10                 48.50
            Transactions:                  0.43

  % Blocks changed per Read:   14.80    Recursive Call %:   34.45
 Rollback per transaction %:    0.00       Rows per Sort:   20.57

Redo size: 是日誌的生成量,分為每秒和每事務所產生的,通常在很繁忙的系統中日誌生成量可能達到上百k,甚至幾百k;

Logical reads: 邏輯讀實際上就是logical IO=buffer gets表示的含義,我們可以這樣認為,block在記憶體中,我們每一次讀一塊記憶體,就相當於一次邏輯讀;

Parses 和 Hard parses:  Parse 和 hard parse通常是很容易出問題的部分,80%的系統的慢都是由於這個原因所導致的。
所謂parse分soft parse 和hard parse,soft parse是當一條sql傳進來後,需要在shared pool中找是否有相同的sql,如果找到了,那就是soft parse,如果沒有找著,那就開始hard parse,實際上hard parse主要是檢查該sql所涉及到的所有的物件是否有效以及許可權等關係,hard parse之後才根據rule/cost模式生成執行計劃,再執行sql。
而hard parse的根源,基本都是由於不使用bind var所導致的,不使用bind var違背了oracle的shared pool的設計的原則,違背了這個設計用來共享的思想,這樣導致shared_pool_size裡面命中率下降。因此不使用bind var,將導致cpu使用率的問題,極有使得效能急劇下降。
還有就是為了維護internal structure,需要使用latch,latch是一種Oracle低階結構,用於保護記憶體資源,是一種內部生命週期很短的lock,大量使用latch將消耗大量的cpu資源。

Sorts: 表示排序的數量;

Executes: 表示執行次數;

Transactions: 表示事務數量;

Rollback per transaction %: 表示資料庫中事務的回退率。如果不是因為業務本身的原因,通常應該小於10%為好,回退是一個很消耗資源的操作。


Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           Buffer Nowait %:  100.00       Redo NoWait %:   99.98
           Buffer  Hit   %:   65.82    In-memory Sort %:   99.65
           Library Hit   %:   91.32        Soft Parse %:   88.18
         Execute to Parse %:    9.28         Latch Hit %:   99.99
Parse CPU to Parse Elapsd %:   94.61     % Non-Parse CPU:   99.90

Buffer Hit %: 資料緩衝區命中率,通常應該大於90%;

Library Hit %: libaray cache的命中率,通常應該大於98%;

In-memory Sort %: 排序在記憶體的比例,如果這個比例過小,可以考慮增大sort_area_size,使得排序在記憶體中進行而不是在temp表空間中進行;

Soft Parse %: 軟解析的百分比,這個百分比也應該很大才好,因為我們要儘量減少hard parse。 soft parse 百分比=soft/(soft+hard);

Execute to Parse %: 這個數字也應該是越大越好,接近100%最好。有些報告中這個值是負的,看上去很奇怪。事實上這表示一個問題,sql如果被age out的話就可能出現這種情況,也就是sql老化,或執行alter system flush shared_pool等。


Shared Pool Statistics          Begin   End
                             ------   ------
         Memory Usage %:    90.63   87.19
   % SQL with executions>1:   71.53   75.39
 % Memory for SQL w/exec>1:  59.45   65.17

% SQL with executions>1: 這個表示SQL被執行次數多於一次的比率,也應該大為好,小則表示很多sql只被執行了一次,說明沒有使用bind var;

2.2.3  等待事件分析
接下來,statspack報告中描述的是等待事件(Wait Events),這是Oracle中比較複雜難懂的概念。
Oracle 的等待事件是衡量Oracle 執行狀況的重要依據及指標。
等待事件的概念是在Oracle7.0.1.2 中引入的,大致有100 個等待事件。在Oracle 8.0 中這個數目增加到了大約150 個,在Oracle8i 中大約有200 個事件,在Oracle9i 中大約有360 個等待事件。
主要有兩種類別的等待事件,即空閒(idle)等待事件和非空閒(non-idle)等待事件。
空閒事件指Oracle 正等待某種工作,在診斷和優化資料庫的時候,我們不用過多注意這部分事件。
常見的空閒事件有:
? dispatcher timer
? lock element cleanup
? Null event
? parallel query dequeue wait
? parallel query idle wait - Slaves
? pipe get
? PL/SQL lock timer
? pmon timer- pmon
? rdbms ipc message
? slave wait
? smon timer
? SQL*Net break/reset to client
? SQL*Net message from client
? SQL*Net message to client
? SQL*Net more data to client
? virtual circuit status
? client message

非空閒等待事件專門針對Oracle 的活動,指資料庫任務或應用執行過程中發生的等待,這些等待事件是我們在調整資料庫的時候應該關注與研究的。
一些常見的非空閒等待事件有:
? db file scattered read
? db file sequential read
? buffer busy waits
? free buffer waits
? enqueue
? latch free
? log file parallel write
? log file sync

下面接合statspack中的一些等待事件進行講述。

Top 5 Wait Events
~~~~~~~~~~~~~~~~~                              Wait     % Total
Event                                    Waits  Time (cs)   Wt Time
--------------------------------------------           ------------ ------------    -------
db file scattered read                      26,877       12,850   52.94
db file parallel write                         472        3,674   15.13
log file parallel write                         975        1,560    6.43
direct path write                           1,571        1,543    6.36
control file parallel write                     652        1,290    5.31
          -------------------------------------------------------------

db file scattered read: DB檔案分散讀取。這個等待事件很常見,經常在top5中出現,這表示,一次從磁碟讀資料進來的時候讀了多於一個block的資料,而這些資料又被分散的放在不連續的記憶體塊中,因為一次讀進來的是多於一個block的。
通常來說我們可以認為是全表掃描型別的讀,因為根據索引讀表資料的話一次只讀一個block,如果這個數字過大,就表明該表找不到索引,或者只能找到有限的索引,可能是全表掃描過多,需要檢查sql是否合理的利用了索引,或者是否需要建立合理的索引。
當全表掃描被限制在記憶體時,它們很少會進入連續的緩衝區內,而是分散於整個緩衝儲存器中。儘管在特定條件下執行全表掃描可能比索引掃描更有效,但如果出現這種等待時,最好檢查一下這些全表掃描是否必要,是否可以通過建立合適的索引來減少對於大表全表掃描所產生的大規模資料讀取。
對於經常使用的小表,應該儘量把他們pin 在記憶體中,避免不必要的老化清除及重複讀取。

db file sequential read: DB檔案連續讀取。通常顯示單個塊的讀取(通常指索引讀取),表示的是讀進磁碟的block被放在連續的記憶體塊中。
事實上大部分基本代表著單個block的讀入,可以說象徵著 IO 或者說通過索引讀入的比較多。因為一次IO若讀進多個的block,放入連續的記憶體塊的機率是很小的,分佈在不同block的大量記錄被讀入就會遇到此事件。因為根據索引讀資料的話,假設100條記錄,根據索引,不算索引本身的讀,而根據索引每個值去讀一下表資料,理論上最多可能產生100 buffer gets,而如果是full table scan,則100條資料完全可能在一個block裡面,則幾乎一次就讀過這個block了,就會產生這麼大的差異。
這種等待的數目很多時,可能顯示錶的連線順序不佳,或者不加選擇地進行索引。
對於高階事務處理(high-transaction)、調整良好(welltuned)的系統,這一數值很大是很正常的,但在某些情況下,它可能暗示著系統中存在問題。
你應當將這一等待統計量與Statspack 報告中的已知問題(如效率較低的SQL)聯絡起來。檢查索引掃描,以保證每個掃描都是必要的,並檢查多表連線的連線順序。
DB_CACHE_SIZE 也是這些等待出現頻率的決定因素。有問題的雜湊區域(Hash-area)連線應當出現在PGA 記憶體中,但它們也會消耗大量記憶體,從而在順序讀取時導致大量等待。它們也可能以直接路徑讀/寫等待的形式出現。

Free Buffer Wait: 釋放緩衝區。
這種等待表明系統正在等待記憶體中的緩衝,因為記憶體中已經沒有可用的緩衝空間了。如果所有SQL 都得到了調優,這種等待可能表示你需要增大DB_BUFFER_CACHE。釋放緩衝區等待也可能表示不加選擇的SQL 導致資料溢位了帶有索引塊的緩衝儲存器,沒有為等待系統處理的特定語句留有緩衝區。
這種情況通常表示正在執行相當多數量的DML(插入/更新/刪除),並且可能說明DBWR 寫的速度不夠快,緩衝儲存器可能充滿了相同緩衝器的多個版本,從而導致效率非常低。為了解決這個問題,可能需要考慮增加檢查點、利用更多的DBWR 程式,或者增加物理磁碟的數量。

Buffer Busy Wait: 緩衝區忙。
該等待事件表示正在等待一個以unshareable方式使用的緩衝區,或者表示當前正在被讀入buffer cache。也就是當程式想獲取或者操作某個block的時候卻發現被別的程式在使用而出現等待。一般來說Buffer Busy Wait不應大於1%。
檢查緩衝等待統計部分(或V$WAITSTAT),看一下等待是否位於段頭。如果是,可以考慮增加自由列表(freelist,對於Oracle8i DMT)或者增加freelist groups.
其修改語法為:
SQL> alter table sp_item storage (freelists 2);
Table altered。

對於Oracle8i而言,增加freelist引數,在很多時候可以明顯緩解等待,如果使用LMT,也就是 Local Manangement Tablespace,區段的管理就相對簡單還可以考慮修改資料塊的pctused\pctfree值,比如增大pctfree可以擴大資料的分佈,在某種程度上就可以減少熱點塊的競爭。

如果這一等待位於undo header,可以通過增加回滾段(rollback segment)來解決緩衝區的問題。
如果等待位於undo block上,我們可能需要檢查相關應用,適當減少大規模的一致性讀取,或者降低一致性讀取(consistent read)的表中的資料密度或者增大DB_CACHE_SIZE。
如果等待處於data block,可以考慮將頻繁併發訪問的表或資料移到另一資料塊或者進行更大範圍的分佈(可以增加pctfree 值,擴大資料分佈,減少競爭),以避開這個"熱點"資料塊,或者可以考慮增加表中的自由列表或使用本地化管理的表空間(Locally Managed Tablespaces)。
如果等待處於索引塊,應該考慮重建索引、分割索引或使用反向鍵索引。反向鍵索引在很多情況下,可以極大地緩解競爭,其原理有點類似於hash分割槽的功效。反向鍵索引(reverse key index)常建在一些值是連續增長的列上,例如列中的值是由sequence產生的。

為了防止與資料塊相關的緩衝忙等待,也可以使用較小的塊:在這種情況下,單個塊中的記錄就較少,所以這個塊就不是那麼"繁忙";或者可以設定更大的pctfree,使資料擴大物理分佈,減少記錄間的熱點競爭。
在執行DML (insert/update/ delete)時,Oracle向資料塊中寫入資訊,對於多事務併發訪問的資料表,關於ITL的競爭和等待可能出現,為了減少這個等待,可以增加initrans,使用多個ITL槽。
以下是一個生產系統v$waitstat 試圖所顯示的等待資訊:
SQL> select * from v$waitstat where count<>0 or time <>0;
CLASS      COUNT TIME
------------------ ---------- ----------
data block       453   6686
undo header      391   1126
undo block       172      3


latch free: latch釋放
latch 是一種低階排隊機制,用於保護SGA 中共享記憶體結構。
latch就像是一種快速地被獲取和釋放的記憶體鎖。latch用於防止共享記憶體結構被多個使用者同時訪問。如果latch不可用,就會記錄latch釋放失敗(latch free miss)。
有兩種與閂有關的型別:
■ 立刻。
■ 可以等待。
假如一個程式試圖在立刻模式下獲得閂,而該閂已經被另外一個程式所持有,如果該閂不能立刻可用的話,那麼該程式就不會為獲得該閂而等待。它將繼續執行另一個操作。
大多數latch 問題都與以下操作相關:
沒有很好的是用繫結變數(library cache latch)、重作生成問題(redo allocation latch)、緩衝儲存器競爭問題(cache buffers LRU chain),以及buffer cache中的存在"熱點"塊(cache buffers chain)。
通常我們說,如果想設計一個失敗的系統,不考慮繫結變數,這一個條件就夠了,對於異構性極強的系統,不使用繫結變數的後果是極其嚴重的。
另外也有一些latch 等待與bug 有關,應當關注Metalink 相關bug 的公佈及補丁的釋出。
當latch miss ratios大於0.5%時,就應當研究這一問題。
Oracle 的 latch 機制是競爭,其處理類似於網路裡的CSMA/CD,所有使用者程式爭奪latch,對於願意等待型別(willing-to-wait)的latch,如果一個程式在第一次嘗試中沒有獲得latch,那麼它會等待並且再嘗試一次,如果經過_spin_count 次爭奪不能獲得latch, 然後該程式轉入睡眠狀態,持續一段指定長度的時間,然後再次醒來,按順序重複以前的步驟.在8i/9i 中預設值是 _spin_count=2000。
如果SQL語句不能調整,在8.1.6版本以上,Oracle提供了一個新的初始化引數: CURSOR_SHARING,可以通過設定CURSOR_SHARING = force 在伺服器端強制繫結變數。設定該引數可能會帶來一定的副作用,對於Java的程式,有相關的bug,具體應用應該關注Metalink的bug公告。


enqueue
enqueue 是一種保護共享資源的鎖定機制。該鎖定機制保護共享資源,如記錄中的資料,以避免兩個人在同一時間更新同一資料。enqueue 包括一個排隊機制,即FIFO(先進先出)排隊機制。
Enqueue 等待常見的有ST、HW 、TX 、TM 等
ST enqueue 用於空間管理和字典管理的表空間(DMT)的分配。對於支援LMT 的版本,可以考慮使用本地管理表空間,對於Oracle8i,因為相關bug 不要把臨時表空間設定為LMT. 或者考慮預分配一定數量的區。
HW enqueue 指段的高水位標記相關等待;手動分配適當區段可以避免這一等待。
TX 是最常見的enqueue 等待。TX enqueue 等待通常是以下三個問題之一產生的結果。
第一個問題是唯一索引中的重複索引,你需要執行提交(commit)/回滾(rollback)操作來釋放enqueue。
第二個問題是對同一點陣圖索引段的多次更新。因為單個點陣圖段可能包含多個行地址(rowid),所以當多個使用者試圖更新同一段時,等待出現。直到提交或回滾, enqueue 釋放。
第三個問題,也是最可能發生的問題是多個使用者同時更新同一個塊。如果沒有自由的ITL 槽,就會發生塊級鎖定。通過增大initrans 和/或maxtrans 以允許使用多個ITL 槽,或者增大表上的pctfree值,就可以很輕鬆地避免這種情況。
TM enqueue 在DML 期間產生,以避免對受影響的物件使用DDL。如果有外來鍵,一定要對它們進行索引,以避免這種常見的鎖定問題。


Log Buffer Space: 日誌緩衝空間
當你將日誌緩衝(log buffer)產生重做日誌的速度比LGWR 的寫出速度快,或者是當日志轉換(log switch)太慢時,就會發生這種等待。為解決這個問題,可以增大日誌檔案的大小,或者增加日誌緩衝器的大小.
另外一個可能的原因是磁碟I/O 存在瓶頸,可以考慮使用寫入速度更快的磁碟。


log file switch (archiving needed)
這個等待事件出現時通常是因為日誌組迴圈寫滿以後,第一個日誌歸檔尚未完成,出現該等待可能是 IO 存在問題。
解決辦法:
可以考慮增大日誌檔案和增加日誌組
移動歸檔檔案到快速磁碟
調整log_archive_max_processes .


log file switch (checkpoint incomplete): 日誌切換(檢查點未完成)
當你的日誌組都寫完以後,LGWR 試圖寫第一個log file,如果這時資料庫沒有完成寫出記錄在第一個log file 中的dirty 塊時(例如第一個檢查點未完成),該等待事件出現。
該等待事件說明你的日誌組過少或者日誌檔案過小。
你可能需要增加你的日誌組或日誌檔案大小。


Log File Switch: 日誌檔案轉換
所有的提交請求都需要等待"日誌檔案轉換(必要的歸檔)"或"日誌檔案轉換(chkpt.不完全)"。確保歸檔磁碟未滿,並且速度不太慢。 DBWR 可能會因為輸入/輸出(I/O)操作而變得很慢。你可能需要增加更多或更大的重做日誌,而且如果DBWxR 是問題癥結所在的話,可能需要增加資料庫書寫器。


log file sync: 日誌檔案同步
當一個使用者提交或回滾資料時,LGWR 將session 會話的重做由redo buffer 寫入到重做日誌中。
log file sync 必須等待這一過程成功完成(Oracle 通過寫redo log file 保證commit 成功的資料不丟失),這個事件說明提交可能過於頻繁,批量提交可以最大化LGWR 的效率,過分頻繁的提交會引起LGWR頻繁的啟用,擴大了LGWR 的寫代價。
為了減少這種等待事件,可以嘗試每次提交更多的記錄。
將重做日誌置於較快的磁碟上,或者交替使用不同物理磁碟上的重做日誌,以降低歸檔對LGWR的影響。
對於軟RAID,一般來說不要使用RAID 5,RAID5 對於頻繁寫入得系統會帶來較大的效能損失,可以考慮使用檔案系統直接輸入/輸出,或者使用裸裝置(raw device),這樣可以獲得寫入的效能提高。


log file single write
該事件僅與寫日誌檔案頭塊相關,通常發生在增加新的組成員和增進序列號時。頭塊寫單個進行,因為頭塊的部分資訊是檔案號,每個檔案不同。更新日誌檔案頭這個操作在後臺完成,一般很少出現等待,無需太多關注。


log file parallel write
從log buffer 寫redo 記錄到redo log 檔案,主要指常規寫操作(相對於log file sync)。
如果你的Log group 存在多個組成員,當flush log buffer 時,寫操作是並行的,這時候此等待事件可能出現。
儘管這個寫操作並行處理,直到所有I/O 操作完成該寫操作才會完成(如果你的磁碟支援非同步IO或者使用IO SLAVE,那麼即使只有一個redo log file member,也有可能出現此等待)。
這個引數和log file sync 時間相比較可以用來衡量log file 的寫入成本。通常稱為同步成本率。


control file parallel write: 控制檔案並行寫
當server 程式更新所有控制檔案時,這個事件可能出現。
如果等待很短,可以不用考慮。如果等待時間較長,檢查存放控制檔案的物理磁碟I/O 是否存在瓶頸。
多個控制檔案是完全相同的拷貝,用於映象以提高安全性。對於業務系統,多個控制檔案應該存放在不同的磁碟上,一般來說三個是足夠的,如果只有兩個物理硬碟,那麼兩個控制檔案也是可以接受的。在同一個磁碟上儲存多個控制檔案是不具備實際意義的。
減少這個等待,可以考慮如下方法:
減少控制檔案的個數(在確保安全的前提下)
如果系統支援,使用非同步IO
轉移控制檔案到IO 負擔輕的物理磁碟


control file sequential read/ control file single write
控制檔案連續讀/控制檔案單個寫
對單個控制檔案I/O 存在問題時,這兩個事件會出現。
如果等待比較明顯,檢查單個控制檔案,看存放位置是否存在I/O 瓶頸。
使用查詢獲得控制檔案訪問狀態:
select P1 from V$SESSION_WAIT
where EVENT like 'control file%' and STATE='WAITING';
解決辦法:
移動有問題的控制檔案到快速磁碟
如果系統支援,啟用非同步I/O


direct path write: 直接路徑寫
該等待發生在,等待確認所有未完成的非同步I/O 都已寫入磁碟。
你應該找到I/O 操作頻繁的資料檔案,調整其效能。
也有可能存在較多的磁碟排序,臨時表空間操作頻繁,可以考慮使用Local 管理表空間,分成多個小檔案,寫入不同磁碟或者裸裝置。


SQL*Net message from dblink
該等待通常指與分散式處理(從其他資料庫中SELECT)有關的等待。
這個事件在通過DBLINKS 聯機訪問其他資料庫時產生。如果查詢的資料多數是靜態的,可以考慮移動這些資料到本地表並根據需要重新整理,通過快照或者物化檢視來減少跨資料庫的訪問,會在效能上得到很大的提高。


slave wait: 從屬程式等
Slave Wait 是Slave I/O 程式等待請求,是一個空閒引數,一般不說明問題。

2.2.4  High Load SQL 分析
對於一個特定的應用程式或者系統來講,要調整優化其效能,最好的方法是檢查程式的程式碼和使用者使用的SQL語句。
如果使用了 level 5 級別的 snapshot ,那麼statspack生成的報告中就會顯示系統中高負荷SQL語句(High Load SQL)的資訊,而其詳細資訊可以在 stats$sql_summary 表中查到。預設情況下 snapshot 的級別是 level 5。
按照 buffer gets, physical reads, executions, memory usage and version count 等引數的降序排列順序,把SQL語句分為幾個部分羅列在報告中。

2.2.5  報告的其他部分
statspack報告的其他部分包括了 Instance Activity Stats,Tablespace IO Stats,Buffer Pool Statistics,Buffer wait Statistics,Rollback Segment Stats,Latch Activity,Dictionary Cache Stats,Library Cache Activity,SGA breakdown difference 以及 init.ora 引數,等等。目前本文不對這些內容進行詳細討論,請參加其他詳細文件。

2.3 trace session  (……)


2.4 基於成本的優化器技術內幕
Oracle基於成本的優化器(Oracle's cost-based SQL optimizer ,簡稱CBO),是Oracle裡面非常複雜的一個部分, 它決定了Oracle裡面每個SQL的執行路徑。CBO是一項評價SQL語句和產生最好執行計劃的具有挑戰性的工作,所以也使它成Oracle最複雜的軟體組成部分。
眾所周知,SQL的執行計劃,幾乎是Oracle效能調整最重要的方面了。所以想要學會如何調整Oracle資料庫的效能,就要學會如何對SQL進行調整,就需要深入透徹理解CBO。
CBO的執行路徑,取決於一些外部因素,內部的Oracle統計資料,以及資料是如何分佈的。
我們將要討論下面的話題:
CBO的引數:我們從基本的優化器引數開始學習,然後學習每個優化器引數是如何影響Oracle的優化器的執行的。

CBO的統計:這裡我們將討論,使用Analyze或者DBMS_STATS來收集正確的統計資料,對Oracle 優化器而言,是多麼的重要。我們還將學習如何把優化器的統計資料,從一個系統拷貝到另外一個系統,這樣可以確保開發環境和產品資料庫環境下,SQL的執行路徑不會變化。

下面我們開始討論CBO優化模式以及影響CBO的Oracle引數

2.4.1  CBO的引數
CBO受一些重要引數的影響,修改這些引數後可以看到CBO效能上戲劇性的變化。首先從設定CBO的optimizer_mode引數開始,然後討論其他重要引數的設定。

在 Oracle 9i 中,optimizer_mode 引數有四種取值,決定了四種優化模式: rule, choose, all_rows, 和 first_rows,其中 rule 和 choose 兩種模式表示目前已經過時的基於規則的優化器模式(rule-based optimizer,簡稱RBO),所以我們在此著重討論後兩種CBO模式。

優化模式的設定可以在系統級進行,也可以對某個會話(session)進行設定,或者對某個SQL語句進行設定。對應的語句如下:
alter system set optimizer_mode=first_rows_10;
alter session set optimizer_goal = all_rows;
select /*+ first_rows(100) */ from student;

我們首先需要知道對一個SQL語句來說,什麼是最好的執行計劃(the best execution plan)?是使SQL語句返回結果的速度最快,還是使SQL語句佔用系統資源最少?顯然,這個答案取決於資料庫的處理方式。

舉一個簡單的例子,比如有下列SQL語句:
select customer_name
from
   customer
where
   region = 'south'
order by

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25736250/viewspace-703623/,如需轉載,請註明出處,否則將追究法律責任。

相關文章