一、SQL語句執行的過程
1、客戶端輸入SQL語句
2、SQL語句通過網絡(如果應用和ORACELE不在同一臺機器上)到達數據庫實例
3、ServerProcess 接收SQL語句(更多server process詳見“ORACLE服務器進程”)
● 解析:此過程會消耗很多資源——CPU、I/O
● 生成執行計劃 →執行
● 從磁盤(數據文件)中讀入必要的數據塊到SGA的共享數據庫緩衝區buffercache(該塊不在緩衝區時)。
● 根據語句進行相應的操作。如果是刪除或修改操作,則直接在內存中(buffer cache)進行修改。對於把buffer cache中的髒數據寫回磁盤(dbf),和寫日誌等操作,serverprocess並不關心(因爲serverprocess只負責與用戶交互,提供用戶最好的體驗,所以日後的優化和性能監控大多關心server process),而是由分別由DBWR和LGWR後臺程序進行。
● 將結果數據反饋給客戶端
二、數據字典
Oracle中所有的數據字典可以在官方文檔《Reference》中查看。
範例:創建T1表,在數據字典dba_tables中查找其所屬的用戶。
select OWNER,TABLE_NAME from dba_tables where TABLE_NAME like 'T1'; |
OWNER TABLE_NAME ------------------------------ ------------------------------ SYS T1 |
三、shared pool組成
主要的3塊區域:free、library cache、dictionary cache(也叫row cache)。
瞭解以下兩個概念:
● Library Cache:緩存最近被執行的SQL和PL/SQL的相關信息,即存放SQL語句的文本,分析後的代碼及執行計劃。實現常用語句的共享,使用LRU算法進行管理,由以下兩個結構構成:Shared SQL area、Shared PL/SQL area;
● DataDictionary Cache:緩存最近被使用的數據庫定義,即存放有關表,列和其它對象定義及權限。它包括關於數據庫文件、表、索引、列、用戶、權限以及其它數據庫對象的信息。在語法分析階段,Server Process訪問數據字典中的信息以解析對象名和對存取操作進行驗證。數據字典信息緩存在內存中有助於縮短響應時間。
範例:SGA中shared pool的以上三個區域的大小:
select * from v$sgastat where pool='shared pool' and name in ('free memory','library cache','row cache') ; |
POOL NAME BYTES ------------ -------------------------- ---------- shared pool library cache 7626040 shared pool free memory 66712768 shared pool row cache 7480368 |
四、硬解析和軟解析
範例:從v$sysstat視圖中查看當前軟硬解析數目
SQL> col name format a25 --修改列顯示寬度 SQL> select name,value from v$sysstat where name like 'parse%' ; |
NAME VALUE ------------------------- ---------- parse time cpu 664 parse time elapsed 1565 parse count (total) 33811 parse count (hard) 3079 parse count (failures) 11 |
Shared Pool中的兩個概念:
● Chunk:shared pool 中的內存塊。
● Chain:在Shared pool中,可用的chunk (free類型,即Free Chunks) 會被串起來稱爲可用鏈表(這裏稱爲Chain,但多數資料稱爲free list),空閒內存塊都是由空閒列表(free list)統一管理、分配的。每個空閒的chunk都會屬於也只屬於一個空閒列表。空閒列表上的chunk的大小範圍是bucket來劃分的。我們也可以把bucket視爲一種索引,使Oracle在查找空閒塊時,先定位所需的空閒塊在哪個bucket的範圍內,然後在相應的空閒列表中查找。
在free區域和librarycache中chunk都是串起來掛在chain上。解析SQL語句時,先從free(此SQL語句沒有執行計劃時)中遍歷合適的chunk,然後serverprocess將sql text轉化爲ASCII碼,對ASCII碼進行hash運算,產生hash bucket號,存放於剛纔找到的chunk中,之後將chunk中有內容(或者說被修改了的)部分劃分出來掛在library cache中,而如果chunk還有剩餘,則其剩餘部分(碎片)就繼續掛在free區域合適的位置。
( 關於library cache及其管理參見“library cache” ;
關於參數CURSOR_SHARING的詳解見《Reference》 )
● x$ksmsp視圖的簡單應用
因爲x$ksmp中存放着所有的chunk的記錄。所以該視圖的行數即當前chunk的個數。
範例:驗證chunk的增加
SQL> select count(*) from x$ksmsp; |
COUNT(*) ---------- 15693 |
select count(*) from dba_objects; |
COUNT(*) ---------- 50317 |
select count(*) from x$ksmsp; |
COUNT(*) ---------- 15754 |
通過觀察可以發現,執行SQL語句會產生很多的chunk。
五、SQL共享,綁定變量
範例:驗證SQL是否共享
SQL> select /*hello*/ count(*) from t1 where name=1; select /*hello*/ count(*) from t1 where name=2; select /*hello*/ count(*) from t1 where name= 1; -- 多一個空格 |
SQL> select sql_id,sql_text,executions from v$sql where sql_text like '%hello%'; |
SQL_ID SQL_TEXT EXECUTIONS ------------- ------------------------------------------------------- ---------- ab0uwntzqx6sx select sql_id,sql_text,executions from v$sql where sql_ 1 text like '%hello%'
9y5vj2r7z2khy select /*hello*/ count(*) from t1 where name= 1 1 ay7yww74z6wjx select /*hello*/ count(*) from t1 where name=1 1 8szsary053x4d select /*hello*/ count(*) from t1 where name=2 1 |
經過驗證可以發現,3條sql語句產生了3個不同的SQL_ID,說明它們並沒有實現共享,而是各自都執行了一次!要達到SQL共享必須SQL語句完全相同——包括空格、大小寫、數值、回車符等等。如果沒有共享,則必須執行硬解析。所以爲了可以共享SQL語句,應該做到以下兩點:
1、統一書寫風格
2、使用綁定變量
範例:通過綁定變量到達SQL語句共享
先通過以下語句將Shared_pool裏面的緩存清空,注意使用此語句必須十分謹慎,因爲清空緩存意味着清空了所有SQL共享,在之後的操作中必定會進行大量的硬解析。
altersystem flush shared_pool;
SQL> declare v_sql varchar2(50); 2 begin for i in 1..10000 loop 3 v_sql := 'insert /*hello*/ into t1 values (:1)'; 4 execute immediate v_sql using i; 5 end loop; 6 commit; 7 end; 8 / |
SQL> select sql_id,sql_text,executions from v$sql where sql_text like '%hello%'; |
SQL_ID SQL_TEXT EXECUTIONS ------------- ------------------------------------------------------- ---------- 1vx8fabpy9dwv select sql_id,sql_text,executions from v$sql where sql_ 1 text like '%hello%'
5wsw3kg839stc declare v_sql varchar2(50); begin for i in 1..10000 loo 1 p v_sql := 'insert /*hello*/ into t1 values (:1)'; exec ute immediate v_sql using i; end loop; commit; end;
f0na2j20ngh92 insert /*hello*/ into t1 values (:1) 10000 |
通過觀察發現,插入這條語句執行了10000次,說明該SQL語句已共享。
(關於綁定變量的實驗參考“oracle中的軟解析和硬解析”)
六、找出沒有共享的SQL語句
可參考以下語句:
select sql_fulltext from v$sql where execution=1 andsql_text like '&from t1%';
但應該注意到此處不一定能說明該語句沒有共享,有可能該語句就只執行了一次而已。或可以參考:
select sql_fulltext from v$sql where execution=1 order bysql_text;
可以把結果數據輸出到文件進行觀察分析,步驟如下:
SQL> spool 1.lst |
SQL> Select sql_fulltext from v$sql where executions=1 order by sql_text; |
SQL> sool off |
SQL> exit |
$ ls |
可以發現外面多了一個1.lst文件,可以用各種閱讀器整理後查看。
七、解析命中率
library命中率:
SQL> select sum(pinhits)/sum(pins)*100 from v$librarycache; |
SUM(PINHITS)/SUM(PINS)*100 -------------------------- 89.705768 |
rowcache命中率:
SQL> select sum(gets),sum(getmisses),100*sum(gets-getmisses)/sum(gets) from v$rowcache where gets>0; |
SUM(GETS) SUM(GETMISSES) 100*SUM(GETS-GETMISSES)/SUM(GETS) ---------- -------------- --------------------------------- 181955 6197 96.5942129 |
實際生產中命中率一般在99%以上,對於查看數據庫命中率,需要在數據庫運行一段時間後再分析才比較準確。
八、4031錯誤
出現4031錯誤的一般原因:大量的硬解析而導致碎片的增多(小而多的chunk),或者大量的硬解析突然後又出現很大的SQL語句,由於存在不到合適的chunk而導致此錯誤。
解決方案:
1、刷新shared_pool
清空shared_pool緩存,使用此方法需十分慎重,而且治標不治本
● alter system flush shared_pool;
2、共享SQL
將cursor_sharing的值改爲FORCE,意味着強制使用綁定變量。(關於cursor_sharing詳見“Oracle cursor_sharing 參數詳解”)
SQL> alter system set cursor_sharing='FORCE'; -- 默認值爲EXACT |
SQL> show parameter cursor_sharing |
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ cursor_sharing string force |
3、強行緩存。
通過查找分析,將大的SQL語句強行緩存到緩存裏面,而且其永遠不會被置換出來。
步驟如下(未經檢驗):
select * from v$db_object_cache where sharable_men > 10000 and (type = 'PACKAGE' or type = 'PACKAGE BODY'or type = 'FUNCTION' or type = 'PROCEDURE') and kept = 'NO' ; |
執行dbms_shared_pool.keep('對象名'); |
DBMS_SHARED_POOL |
@?/rdbms/admin/dbmspool.sql |
根據ORACLE對shared_pool的管理來設置“強行緩存”。ORACLE對shared_pool管理時,如果在free區域中剩下的都是很小的chunk,而此時又遇到大SQL時,ORACLE會在library cache中把使用頻率少的大SQL緩存“擠”出去,然後把這塊的緩存空間重新掛回free區域中。但此時還有可能再次遇到問題,如果被“擠”出來的SQL語句再次被執行,而在free區域和library cache中再也找不到合適的緩存時,就會發生4031錯誤。
對此,解決的辦法就是,通過對library cache中的chunk進行排序,把最大的前幾個chunk強行放到緩存中,這樣就可以避免了4031錯誤。
4、保留區
保留區(shared_pool_reserved,也叫保留池)是專門用於存放shared_pool中大對象的空間。如果對象大於特定的值,就會向shared_pool_reserved請求空間,但是如果在shared_pool_reserved中也找不到空間的話就會出現4031錯誤。
SQL>select request_misses from v$shared_pool_reserved;
REQUEST_MISSES
--------------
0
SQL>show parameter shared_pool_reserved
NAME TYPE VALUE
---------------------------- ----------- -------------------
shared_pool_reserved_size biginteger 9856614
關於REQUEST_MISSES參數官方說明是:
Number oftimes the reserved list did not have a free piece of memory to satisfy therequest, and started flushing objects from the LRU list.(無法滿足查找保留區空閒內存塊請求,需要從LRU列表中清出對象的次數。)
(更多參考“共享池中保留池的調整(shared_pool_reserved_size)”)
5、shared_pool擴容
因爲在ORACLE中我們不能對shared_pool裏面的各個區域進行空間的調整,所以只能通過增加shared_pool的空間從而讓ORACLE自動調整各個區域的空間。
調整shared_pool空間需注意,如果將shared_pool空間調大,那麼ORACLE會立刻調大其空間;但是如果將其調小,ORACLE並不會立刻將shared_pool的空間調小,而是運行一段時間後,如果確定shared_pool並不需要那麼大的空間時,纔將其調小。
範例:動態調整shared_pool大小
查看shared_pool調整前的大小:0M,即由SGA_TARGET自動調整
SQL> show parameter shared_pool_size |
NAME TYPE VALUE ------------------ ------------------ -------------- shared_pool_size big integer 0 |
雖然此處shared_pool的大小爲0M,但實際shared_pool並不可能是0M,通過查看v$sga_dynamic_components數據字典可以發現其值。
SQL> select component,current_size from v$sga_dynamic_components; |
COMPONENT CURRENT_SIZE ---------------------------------------------------------------- ------------ shared pool 201326592 large pool 4194304 java pool 4194304 streams pool 0 DEFAULT buffer cache 385875968 KEEP buffer cache 0 RECYCLE buffer cache 0 DEFAULT 2K buffer cache 0 DEFAULT 4K buffer cache 0 DEFAULT 8K buffer cache 0 DEFAULT 16K buffer cache 0 0DEFAULT 32K buffer cache 0 ASM Buffer Cache 0
13 rows selected. |
通過觀察,我們可以發現,當前shared_pool的大小是200M左右。
下面來動態調整shared_pool的大小,但其值並不大於200M,看能否成功。
SQL> alter system set shared_pool_size=20M scope=both ; |
SQL> show parameter shared_pool_size |
NAME TYPE VALUE ------------------ ------------------ -------------- shared_pool_size big integer 20M |
SQL> select component,current_size from v$sga_dynamic_components; |
COMPONENT CURRENT_SIZE ---------------------------------------------------------------- ------------ shared pool 201326592 large pool 4194304 java pool 4194304 streams pool 0 DEFAULT buffer cache 385875968 KEEP buffer cache 0 RECYCLE buffer cache 0 DEFAULT 2K buffer cache 0 DEFAULT 4K buffer cache 0 DEFAULT 8K buffer cache 0 DEFAULT 16K buffer cache 0 0DEFAULT 32K buffer cache 0 ASM Buffer Cache 0
13 rows selected. |
可以發現如果shared_pool大小調整爲小於當前的大小,ORACLE並不會立刻將其值調小,但如果調大呢?
SQL> alter system set shared_pool_size=300M scope=both ; |
SQL> show parameter shared_pool_size |
NAME TYPE VALUE ------------------ ------------------ -------------- shared_pool_size big integer 300M |
SQL> select component,current_size from v$sga_dynamic_components; |
COMPONENT CURRENT_SIZE ---------------------------------------------------------------- ------------ shared pool 314572800 large pool 4194304 java pool 4194304 streams pool 0 DEFAULT buffer cache 272629760 KEEP buffer cache 0 RECYCLE buffer cache 0 DEFAULT 2K buffer cache 0 DEFAULT 4K buffer cache 0 DEFAULT 8K buffer cache 0 DEFAULT 16K buffer cache 0 DEFAULT 32K buffer cache 0 ASM Buffer Cache 0
13 rows selected. |
可以發現,此時shared_pool已經調大了。此時,問題又來了,在ORACLE 10g中,SGA是自動管理的,這樣的好處是可以動態分配SGA中組件的內存的大小,不必造成內存浪費。而SGA的大小可以有動態參數SGA_TARGET設置,但是如果這個參數設置的大小大於物理內存則有可能出現錯誤,所以ORACLE還引入了另外一個參數:SGA_MAX_SIZE,這個參數限制SGA_TARGET的大小不能大於它。(更多詳見“SGA_MAX_SIZE與SGA_TARGET”)
九、如何設置shared_pool及SGA大小
shared_pool的大小並非越大越好,過大的shared_pool可能會帶來其他一系列問題。下面進行簡單分析:
shared_pool大 → library cache大 → chain(bucket)多 → 每條chain上的chunk多 → 每條SQL在chunk上遍歷的時間長 → 遍歷時要鎖住當前的chain → 導致資源爭用。
而對於shared_pool的大小可以通過兩種方式來設置。
● 由ORACLE自動(默認)設置
通過設置SGA_TARGET的大小,讓ORACLE自動管理SGA,讓部分區的內存大小動態共享起來(只包括Buffer cache、Shared pool、Large pool、Java pool、Stream pool,而其他的區的內存大小依然的固定不共享的)。
(參考“SGA_MAX_SIZE與SGA_TARGET”)
● 手動設置
(1)通過SQL查詢語句來分析並確定其值大小。
SQL>SELECT 'Shared Pool'component,shared_pool_size_for_estimate estd_sp_size,
2 estd_lc_time_saved_factor parse_time_factor,
3 CASE
4 WHEN current_parse_time_elapsed_s + adjustment_s < 0
5 THEN 0
6 ELSE current_parse_time_elapsed_s + adjustment_s
7 END response_time
8 FROM
9 ( SELECT shared_pool_size_for_estimate,shared_pool_size_factor,
10 estd_lc_time_saved_factor,a.estd_lc_time_saved,
11 e.VALUE / 100 current_parse_time_elapsed_s,
12 c.estd_lc_time_saved - a.estd_lc_time_saved adjustment_s
13 FROM v$shared_pool_advice a,
14 (SELECT * FROM v$sysstat WHERE NAME = 'parse time elapsed') e,
15 (SELECT estd_lc_time_saved FROM v$shared_pool_advice WHEREshared_pool_size_factor = 1) c
16 );
COMPONENT ESTD_SP_SIZEPARSE_TIME_FACTOR RESPONSE_TIME
-------------------------------------------- ----------------- -------------
SharedPool 240 .9966 69.84
SharedPool 272 1 61.84
SharedPool 304 1 61.84
SharedPool 336 1 61.84
SharedPool 368 1 61.84
SharedPool 400 1 61.84
SharedPool 432 1 61.84
SharedPool 464 1 61.84
SharedPool 496 1 61.84
SharedPool 528 1 61.84
SharedPool 560 1 61.84
SharedPool 592 1 61.84
SharedPool 624 1 61.84
(2)通過EM設置
指導中心 → 內存指導 → SGA 建議
在ORACLE 10g以前一般建議設置shared_pool ≤ 1 G,但在ORACEL 10g以後允許有多個sub shared pool,所以可以設置大於1 G的shared pool。