SQL語句執行過程到shared pool剖析

一、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。

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