1. 程式人生 > >理解AWR效能報告中的Execute to Parse%指標

理解AWR效能報告中的Execute to Parse%指標

這個是我生存庫的指標,感覺指標好低啊! 這個指標的意思是說 執行次數與解析次數的對比. 按這個意思來說,如果解析1次執行1次 ,這個比例應該為100%。
帶著這個理解,解析1次執行1次基本上算是硬解析。而我的系統裡面繫結變數的SQL也蠻多的啊,這個指標應該高於100%。
這個指標公式 Execute to Parse %: dscr , round(100*(1-:prse/:exe),2) pctval
比如SQL解析100次,執行150次,換算過來就是1-100/150=1-66.7約 23%; 如SQL解析100次,執行10000次,換算是1-100/10000=1-0.01=99%;
如果是解析1次執行1次的話 100*(1-1/1)=0%
原來標準值是0%,高於這個值的才算是好的系統,它無限接近於100%。 看來是我的大腦的誤解!
我們看下解析過程如圖

這裡寫圖片描述

解析是什麼鬼? 解析也可以說是編譯。所謂編譯就是把我們人寫的字元程式,轉換成計算機能執行的二進位制程式碼,也就是101000111。在大學的計算機科班專業的同學,一定學過編譯原理這門課。知道編譯要做很多事情! 做過開發的人也知道,當專案程式碼約多,編譯速度越慢。就算一個簡單的小專案編譯一次也要個1分鐘的時間。何況是資料塊伺服器,每天光一條SQL都要執行上萬次,一天要執行上千乃至上萬條不同的SQL語句。要是每條語句編譯一次消耗1秒時間,那麼1億次,需要2萬7777小時。需要多少個強勁的CPU才能一內完成編譯工作呢? CPU除了編譯難道就不要做其他工作了嗎?
所以節約編譯時間是頭等大事! Execute to Parse 這個指標就是衡量編譯節約時間的。如果執行一次編譯一次就0%值!!

好了 我們想下如何節約編譯! 1 如果一條SQL語句 一樣的 比如測試資料庫是否存活的語句,select sysdate from dual; 這個語句應用程式每個1分鐘執行一次,一天要執行360次.
如果每次編譯消耗1秒,就無所謂啦.懶得理它呢. 既然它要執行那麼多次,我們是否可以把它快取起來呢? 暫時放在記憶體裡, 裡面包含SQL語句和對應的執行計劃,也就是編譯後的二進位制程式碼. 這樣就好了下次再來,就從記憶體中把它給取出來執行就是了.
記憶體快取這個辦法 對應上面的圖的黃色路徑.也就是從PGA白色箭頭 到SGA裡面的SQL執行計劃管理區, 通過黃色箭頭 再到SQL執行計劃快取區. 最後就直接執行.

為了提供記憶體快取辦法的命中率,也就是說除了同樣一條語句執行次數外,我們要讓類似的語句也快取起來.這樣就提高了命中率.
比如上面說的SQL語句 select sysdate from dual; 這樣的語句太特別了,屬於特例. 實際中我們不可能出現 重複執行 這樣的 select 身份證ID from 全國身份證資訊表 where 姓名=’小凡仙’ . 這樣的語句我們頂多執行5-6次. 大量的確實這樣的類似語句
select 身份證ID from 全國身份證資訊表 where 姓名=’小凡仙’;
select 身份證ID from 全國身份證資訊表 where 姓名=’中凡仙’;
select 身份證ID from 全國身份證資訊表 where 姓名=’大凡仙’;
select 身份證ID from 全國身份證資訊表 where 姓名=’張果老’;
select 身份證ID from 全國身份證資訊表 where 姓名=’南海觀音’;
select 身份證ID from 全國身份證資訊表 where 姓名=’東海龍王’;

這樣的語句在我們人類看來是一條,而JAVA程式碼裡也是寫的一條.可在計算機眼裡是不一樣的SQL。唯一區別就是查詢的姓名的條件不一樣而已。然後我們再對比它們的執行計劃也是一樣的。
如果有什麼辦法把這些類似的語句,變成一條,尤其是在計算機眼裡認為是一條,然後從記憶體在拿到已經編譯好的執行計劃。
在計算機眼裡怎麼認為它是呢? 在ORACLE資料庫它這樣認為的,先給你發來的SQL字串用HASH函式轉換成數字序列。比如說13345670346489435432435. 然後跟記憶體中的對比下。 也就是說兩個類似的SQL語句 之間 任何個字元不一樣就是不同的,比如說 多了個空格,多了個字元,這個字元是小寫的。
所以 我們把類似語句不一樣的地方,變成一樣的。如下,把上面6條類似的用一個英文單詞來替代不一樣的地方。
select 身份證ID from 全國身份證資訊表 where 姓名=:Name;

這就是傳說中的繫結變數 不過這個名詞有點拗口,也有點晦澀。對於有過開發,或者說編過程的人來講,不太好理解。如果說是函式的引數化 就比較好理解。
例如寫個函式
void call_my_iphone(iphone_num:number) { ……};
呼叫的時候
call_my_iphone(13828876883);

我們上面那樣子寫 如同寫了個函式. 然後再繫結引數進去.
實際上引數在SQL執行的時候,也就是最後一步才把引數帶進去.
這樣我們把大部分的SQL語句的編譯後的執行計劃給快取起來了.同時提高了快取中的命中率.
這個繫結變數的方法有個缺陷,不是所有的語句可以採用這個方法的. 這個方法的前提是 類似的語句必須擁有相同的執行計劃.

如果快取了很多SQL語句的執行計劃,比如說1萬條的話,我們發現執行計劃的管理區將是個嚴重的負擔。 ORACLE 採用多個鏈條 這個數大概是個內部值。也序是128根鏈條。你可以想象是晒衣服的繩子。這128個繩子上均勻的晒了79個衣服。 我們要找到其中所需要的衣服來穿,我們太懶了,衣服多的要死,直接掛在外面晒,同時又當衣櫃。不會下雨的,因為外面全是玻璃罩著。當然我們人眼立刻定位到所需要的衣服。其實我們人眼也要從繩子一頭掃描到繩子一尾。 如果這個繩子晒的不同的衣服,不同的顏色,不同款式,就沒有這個必要了。然並卵,衣服已經把相同的款式,相同的顏色晒到一個繩子上。 這個怎麼可能啊? 不! 這是公共晒衣兼衣櫃,除了你,你住的鄰居們都在晒。這下好了,一條繩子上除了你的,還有別人的,唯一區別就是衣架上有你的名字。所以你必須從繩頭掃描到過去。
好了,我們再想象下,到了早上,大家都要去拿衣服來穿,晒衣區不夠寬,這個區儘可能的區域用於晒衣服。早上有10個男人同時要拿白色的存衣來穿。可見他們的衣服被晒在一個繩子上。如果這10個是低素質的男人,他們會一起上去搶,然後打打出手。所以安排了LOCK管理員,LATCH管理員,MUTEX管理員,然後給他們發牌子一個個進去拿。
三個管理員 要處理1萬個人拿衣服的請求,自然也負擔很重了。除了拿衣服有長有短的時間外,人多了就有個排隊領牌子的時間,這個令牌子的時間隨著人多,時間就越長。

你想我給管理員塞點錢,溝通下。今天領到這個牌子,是否可以我留著用啊?我保證我的白色寸衣掛在某個晒衣繩子,第56個衣架上。 管理員說也行,不過牌子是留給你不好,別人會說的,我給你搞個綠色通道,VIP卡。卡上說明你的白色寸衣地址。
這就是上圖上的綠色箭頭。傳說中的軟軟解析

上圖左邊的PGA 有兩個引數 分別是OPEN_CURSOR 和SESSION_CACHE_CURSOR。表示每個人可以開多少個VIP卡。一次使用多少張VIP卡。比如說每個可以開20張,同時只能使用5張卡。

要獲得VIP卡,必須跟管理員搞好關係,塞點錢賄賂下。 我們ORACLE 通過會話,執行了3次才有資格。前提必須有空位。一個會話是否會執行3次以上呢? JAVA搞得連線池的目的是減少連線的申請,頻繁的申請會導致資料庫LINUX程序的工作量。它只是為了這個目的而已,叫連線重用。 在ORACLE 基本上一個連線算是一個會話,當這個連線程序會被隨機分配執行不同的功能,因此重複執行一樣SQL語句的機會比較低。

檢視當前系統session配置

Select 'session_cached_cursors' Parameter,
    Lpad(Value, 5) Value,
    Decode(Value, 0, ' n/a', To_Char(100 * Used / Value, '990') || '%') Usage
From (Select Max(s.Value) Used
            From V$statname n, V$sesstat s
           Where n.Name = 'session cursor cache count'
           And s.Statistic# = n.Statistic#),
    (Select Value From V$parameter Where Name = 'session_cached_cursors')
Union All
Select 'open_cursors',
    Lpad(Value, 5),
    To_Char(100 * Used / Value, '990') || '%'
From (Select Max(Sum(s.Value)) Used
      From V$statname n, V$sesstat s
     Where n.Name In
        ('opened cursors current', 'session cursor cache count')
      And s.Statistic# = n.Statistic#
     Group By s.Sid),
    (Select Value From V$parameter Where Name = 'open_cursors');
PARAMETER              VALUE    USAGE
---------------------- ------- -----

session_cached_cursors 50 98% --當前的使用率為98%,應考慮增加該引數值 open_cursors          300 20% --當前open_cursors僅為20%,說明當前夠用
========================================================================

影響EXE/PARSE的指標有如下方面
1 JAVA方面的連線池的連線通道分配演算法不夠好
2 JAVA 連線池動態調整,比如說減少連線數。從100減少到50. 連線數的減少,意味著會話被終止了。就無法快取VIP卡。
3 沒有被繫結變數,命中率很低。只能硬解析
4 記憶體大小的變化,因為有個自動記憶體的管理,會逼迫執行計劃快取區減少,自然要釋放掉部分快取內容。
5 上線,一個應用系統經常會被版本更新,每次更新就會代來新的SQL,擠掉舊的SQL。

– 也可以通過下面的腳步檢視cursor的使用情況

SQL> SELECT MAX(A.VALUE) AS HIGHEST_OPEN_CUR, P.VALUE AS MAX_OPEN_CUR  
  2   FROM V$SESSTAT A, V$STATNAME B, V$PARAMETER P  
  3  WHERE A.STATISTIC# = B.STATISTIC#  
  4    AND B.NAME = 'opened cursors current'  
  5    AND P.NAME = 'open_cursors'  
  6  GROUP BY P.VALUE;
HIGHEST_OPEN_CUR MAX_OPEN_CUR
---------------- ---------------------------------------------
300              19           --檢視cursor相關統計值,例項級別

SQL> select name,value from v$sysstat where name like '%cursor%';
NAME                                VALUE
----------------------------------- ----------
opened cursors cumulative            819271677
opened cursors current                     350
pinned cursors current                       6
session cursor cache hits            340959054
session cursor cache count           399411460
cursor authentications                   56465

–提供快取值

alter system set session_cached_cursors=300 scope=spfile;

修改後要重啟資料庫方能生效。
–通過下面語句找出執行比差的

 SELECT  st.sql_id,
         st.executions_total,
         st.parse_calls_total,
         ROUND (100 * (1 - (st.parse_calls_total / st.executions_total)), 2)||'%' AS execute_to_parse,
         st.executions_delta,
         st.parse_calls_delta,
         ROUND (100 * (1 - (st.parse_calls_delta / st.executions_delta)), 2)||'%' AS  delta_ratio
    FROM DBA_HIST_SQLSTAT st, DBA_HIST_SQLTEXT sq, DBA_HIST_SNAPSHOT s
   WHERE     s.snap_id = st.snap_id
         AND s.begin_interval_time >=
                TO_DATE ('2017-01-01 09:30:00', 'YYYY-MM-DD HH24:MI:SS')
         AND s.end_interval_time <=
                TO_DATE ('2017-03-22 17:00:00', 'YYYY-MM-DD HH24:MI:SS')
         AND st.sql_id = sq.sql_id
         AND st.parsing_schema_name in ('小凡仙')
         AND st.executions_total != 0
         AND st.executions_delta != 0
 ORDER BY delta_ratio ;

V$SQLAREA 欄位解釋:
PARSE_CALLS 解析的次數
LOADS 載入的次數,即硬解析的次數
EXECUTIONS 執行的次數

下面是軟軟解析

begin
for i in 1 .. 10
loop 
    execute immediate 'select * from A1 t where ourcode=:sb' using i;
end loop;
end;

select sql_id, a.LOADS,a.EXECUTIONS, a.PARSE_CALLS, a.SQL_TEXT
  from v$sqlarea a
 where sql_id = 'ah4tdp2ksucnu';
SQL_ID               LOADS  EXECUTIONS  PARSE_CALLS SQL_TEXT
ah4tdp2ksucnu       1         20        2   select * from A1 t where ourcode=:sb

可以看到 解析次數明顯小於執行次數

軟體解析:
SQL> var oid number;
SQL> exec :oid:=20;
SQL> select * from test where object_id=:oid;
未選定行
SQL> exec :oid:=30;
SQL> select * from test where object_id=:oid;
未選定行
SQL> exec :oid:=40;
SQL> select * from test where object_id=:oid;
未選定行
SQL> exec :oid:=50;
SQL> select * from test where object_id=:oid;
未選定行
SQL_TEXT                           PARSE_CALLS LOADS EXECUTIONS
select * from test where object_id=:oid 4      1       4

這裡有個疑問 就是我們在SQLPLUS裡面多次執行同一條SQL語句,為什麼不是軟軟解析呢?哪怕執行了20次,也是軟解析,同樣執行了20次。那麼這樣也無法提高該指標啊!!
因此軟軟解析的條件要求比較高.前面兩個條件只是基礎條件.
這個疑問我們下次再聊.
請關注公眾號:
這裡寫圖片描述