UPDATE T1
SET SMALL_NO = SMALL_NO + 0.1
WHERE ID = 3
AND SMALL_NO = 1
SQL> create table t1 ( id number, small_no number ) ;
Table created.
SQL> exec dbms_stats.gather_table_stats(user,'T1');
PL/SQL procedure successfully completed.
SQL> alter system flush shared_pool ;
System altered.
-- 이전 SQL 문장의 라이브러리 캐시로부터 제거
-- 오브젝트에 대한 권한 및 종속성, 오브젝트 정의를 딕셔네리 캐시로부터 제거
SQL> alter session set sql_trace = true ;
Session altered.
SQL> update t1 set small_no = small_no + 0.1
2 where id = 3 and small_no = 1 ;
0 rows updated.
1 session in tracefile.
2 user SQL statements in trace file.
-- ( 유저가 수행한 1) alter session... 2) update 문장 )
15 internal SQL statements in trace file.
-- ( sys recursive 문장 : DBMS 가 Update 문을 이해(Parse) 및 최적화하기 위한 정보를 수집)
17 SQL statements in trace file.
17 unique SQL statements in trace file.
737 lines in trace file.
OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 15 0.01 0.00 0 0 0 0
Execute 99 0.06 0.05 0 0 0 0
Fetch 189 0.00 0.01 0 338 0 633
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Total 303 0.10 0.07 0 338 0 633
-- 15개의 SQL문에 대한 parsel call 15회 수행
-- execute call 99 회 수행,
-- fetch call 189 회 수행을 통해서 633 Row 추출
Misses in library cache during parse: 15
-- Miss 발생은 자식 커서(실행계획)가 라이브러리 캐시 내에 존재 하지 않음 의미
-- Recursive SQL 문의 첫 번째 parse call 시에 최적화 필요함
SQL> create table t1a ( id number, small_no number ) ;
Table created.
SQL> exec dbms_stats.gather_table_stats(user,'T1');
-- PL/SQL procedure successfully completed.
-- 이후 동일한 테스트 수행 (Shared Pool Flush 하지 않는다.)
OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 15 0.00 0.00 0 0 0 0
Execute 43 0.01 0.01 0 0 0 0
Fetch 51 0.00 0.00 0 135 0 526
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Total 109 0.00 0.00 0 133 0 526
Misses in library cache during parse: 0
-- 1차 테스트와 동일한 Parse call= 15 발생
-- 그러나, execute 와 fetch call 횟수가 다름
-- Miss 0 발생은 이미 적절한 커서가 라이브러리 캐시 내에 존재
-- SQL 문장이 참조하는 Object 정의 찾는 Recursivce SQL
selectobj#,type#,ctime,mtime,stime,status,dataobj#,flags,oid$, spare1,
spare2
from obj$ where owner#=:1 and name=:2 and namespace=:3 and remoteowner is null
andlinkname is null and subname is null
-- SQL 문장이 참조하는 테이블 정의 찾는 Recursivce SQL
select t.ts#,t.file#,t.block#,nvl(t.bobj#,0),nvl(t.tab#,0),t.intcols,
nvl(t.clucols,0),t.audit$,t.flags,t.pctfree$,t.pctused$,t.initrans,
t.maxtrans,t.rowcnt,t.blkcnt,t.empcnt,t.avgspc,t.chncnt,t.avgrln,
t.analyzetime,t.samplesize,t.cols,t.property,nvl(t.degree,1),
nvl(t.instances,1),t.avgspc_flb,t.flbcnt,t.kernelcols,nvl(t.trigflag, 0),
nvl(t.spare1,0),nvl(t.spare2,0),t.spare4,t.spare6,ts.cachedblk,ts.cachehit,
ts.logicalread
from tab$ t, tab_stats$ ts where t.obj#= :1 and t.obj# = ts.obj# (+)
-- SQL 문장이 참조하는 인덱스 정의 찾는 Recursivce SQL
select i.obj#,i.ts#,i.file#,i.block#,i.intcols,i.type#,i.flags,i.property,
i.pctfree$,i.initrans,i.maxtrans,i.blevel,i.leafcnt,i.distkey,i.lblkkey,
i.dblkkey,i.clufac,i.cols,i.analyzetime,i.samplesize,i.dataobj#,
nvl(i.degree,1),nvl(i.instances,1),i.rowcnt,mod(i.pctthres$,256),
i.indmethod#,i.trunccnt,nvl(c.unicols,0),nvl(c.deferrable#+c.valid#,0),
nvl(i.spare1,i.intcols),i.spare4,i.spare2,i.spare6,decode(i.pctthres$,null,
null,mod(trunc(i.pctthres$/256),256)),ist.cachedblk,ist.cachehit,
ist.logicalread
from ind$ i, ind_stats$ ist, (select enabled, min(cols) unicols,
min(to_number(bitand(defer,1))) deferrable#,min(to_number(bitand(defer,4)))
valid# from cdef$ where obj#=:1 and enabled > 1 group by enabled) c where
i. obj#=c.enabled(+) and i.obj# = ist.obj#(+) and i.bo#=:1 order by i.obj#
-- SQL 문장이 참조하는 칼럼 정의 찾는 Recursivce SQL
select name,intcol#,segcol#,type#,length,nvl(precision#,0),decode(type#,2,
nvl(scale,-127/*MAXSB1MINAL*/),178,scale,179,scale,180,scale,181,scale,182,
scale,183,scale,231,scale,0),null$,fixedstorage,nvl(deflength,0),default$,
rowid,col#,property, nvl(charsetid,0),nvl(charsetform,0),spare1,spare2,
nvl(spare3,0)
from col$ where obj#=:1 order by intcol#
-- 첫번째 테스트는 다양한 "bootstrap" 활동
-- ( 오라클은 테이블과 인텍스에 대한 정보를 확인하기 위해서, internal 테이블과 인텍스에
-- 대한 몇 가지 정보를 더 확인할 필요가 있다. )
-- 두번째 테스트는 첫번째 테스트에서 캐시된 정보를 이용 (메타데이터와 코드의 캐싱을 의미함)
-- 메타데이타 - row cache : 테이블, 인덱스, 칼럼, 히스토그램, 트리거 등과 같은 오브젝트 정보
-- 코드 - 라이브러리 캐시 : 명령어(SQL문과 PL/SQL), 커서 및 오브젝트 종속성과 권한 정보
SQL> select cache#, parameter, type, subordinate#, count, usage, fixed, gets ,getmisses
from v$rowcache order by cache#, type, subordinate# ;
CACHE# PARAMETER TYPE SUBORDINATE# COUNT USAGE FIXED GETS GETMISSES
---------- -------------------------------- ----------- ------------ ---------- ---------- ---------- ---------- ----------
0 dc_tablespaces PARENT 10 10 0 17216006 87
1 dc_free_extents PARENT 0 0 0 0 0
2 dc_segments PARENT 926 926 0 2761891 47480
3 dc_rollback_segments PARENT 25 25 1 3051290 24
4 dc_used_extents PARENT 0 0 0 0 0
5 dc_tablespace_quotas PARENT 0 0 0 220 32
6 dc_files PARENT 11 11 0 43505 44
7 dc_users SUBORDINATE 0 5 5 0 243337 145
7 dc_users SUBORDINATE 1 33 33 0 1509468 431
7 dc_users SUBORDINATE 2 0 0 0 0 0
8 dc_objects PARENT 1849 1849 116 13042484 168133
8 dc_object_grants SUBORDINATE 0 10 10 0 34906 8087
Select count(*) from all_objects ;
SQL> create or replace view my_objects as select * from user_objects ;
View created.
SQL> select referenced_owner, referenced_name, referenced_type
2 from user_dependencies
3 where name ='MY_OBJECTS';
REFERENCED_OWNER REFERENCED_NAME REFERENCED_TYPE
------------------------------ ------------------------------ -----------------
PUBLIC USER_OBJECTS SYNONYM
OPS$ORACLE USER_OBJECTS NON-EXISTENT
# 현재 스키마에 속하지 않는 user_objects 라고 불리는 실제 오브젝트에 좌우됨
# 만일 user_objects 라는 실제 오브젝트를 생성하면,
오라클은 non-existent 에 대한 종속성 제거(이로써 my_objects뷰 정의 무효화)
11g)
select referenced_owner, referenced_name, referenced_type
from user_dependencies
where name = 'MY_OBJECTS'
/
REFERENCED_OWNER REFERENCED_NAME REFERENCED_TYPE
-------------------- -------------------- ------------------------------
PUBLIC USER_OBJECTS SYNONYM
: v$rowcache뷰의 한 로우와 v$rowcache_parent 및 v$rowcache_subordinate뷰 관계로 이해
[그림 7-1] 딕셔너리 캐시의 subset
SQL> select * from v$sgastat where name like '%KQR%' order by name
POOL NAME BYTES
------------ -------------------------- ----------
shared pool KQR ENQ 90816
shared pool KQR L PO 694960
shared pool KQR L SO 125992
shared pool KQR M PO 2199792
shared pool KQR M SO 549968
shared pool KQR S SO 26128
shared pool KQR X PO 30840
7 rows selected.
딕셔너리 캐시 내의 항목을 찾기 위해 해시 체인을 탐색하는 동안에 해시 체인이 변경되지 않도록 보호해야 한다.
딕셔너리 캐시 활동의 핵심은 해시 버킷과 해시 체인에 근거하며, 해시 체인은 읽기/쓰기 충돌로부터 보호되어야 함
딕셔너리 캐시 및 이와 과련된 래치 활동성을 관찰
테스트 주요 SP 설명
Snap_latch_child – v$latch_children뷰의 스냅샷 제공
Snap_rowcache -- v$rowcache뷰의 스냅샷 제공
결과
Parsing은 Parsing,Parse call로 분리
이를 분리해야 하는 이유는
Name (literal string test) value
Name (bind variable test) value
Name (bind variable test 9i) value
cursor authentication
: 자식 커서의 내용을 비교 (동일 오브젝트여부/동일 권한여부/옵티마이져 환경)
: 커서 캐싱을 디폴트(11g : 50)과 0으로 설정 후, dbms_sql 캐시지를 이용한 명시적 parse 와 execute call 수행
fori in 1..1000 loop
m_cursor := dbms_sql.open_cursor;
dbms_sql.parse(
m_cursor,
'select n1 from t1 where id = :n',
dbms_sql.native
);
dbms_sql.define_column(m_cursor,1,m_n);
dbms_sql.bind_variable(m_cursor, ':n', i);
m_rows_processed := dbms_sql.execute(m_cursor);
ifdbms_sql.fetch_rows(m_cursor) > 0 then
dbms_sql.column_value(m_cursor, 1, m_n);
end if;
dbms_sql.close_cursor(m_cursor);
end loop;
커서 캐싱 동작 방식
문장을 자주 사용한다면, 커서 변수 선언하고, 원하는 만큼 오랫동안 커서를 open 하는것이 바람직
m_cursor := dbms_sql.open_cursor;
dbms_sql.parse(
m_cursor,
'select n1 from t1 where id = :n',
dbms_sql.native
);
dbms_sql.define_column(m_cursor,1,m_n);
fori in 1..1000 loop
dbms_sql.bind_variable(m_cursor, ':n', i);
m_rows_processed := dbms_sql.execute(m_cursor);
ifdbms_sql.fetch_rows(m_cursor) > 0 then
dbms_sql.column_value(m_cursor, 1, m_n);
end if;
end loop;
dbms_sql.close_cursor(m_cursor);
execute call 시 파싱과 최적화 발생 할 경우
: PL/SQL 최적화로 인한 홀딩(Holding) 커서의 부작용
: 캐시 버퍼 체인과 라이브러리 캐시 체인은 근본적으로 동일한 목적 제공
즉, 특정한 항목의 위치를 매우 빠르게 찾는 방안 제공
Shared Pool(sub Pool이나 지속기간에 따라) 나누는 이유
Shared Pool 내에는 permanent, recreatable 및 freeable 청크가 존재 하지만
LRU 리스트(들)에 존재하는 것은 recreatable 청크 뿐이다.
리스트 하단부를 보면 recreatable 청크의 개수는 12,085 개이고, 이 중 3,921 개는 recurrent(rcr)이고 8,164 개는 transient(trn)라는 것을 알 수 있다.
Tansient 는 separator 앞부분에 위치, recurrent 는 separator 뒷 부분에 위치
새로운 항목이 LRU 리스트에 추가되면 Transient 리스트의 헤드에 등록
해당 항목이 한번 이상 사용되면 recurrent 리스트의 헤드로 이동
결과적으로 자주 사용하지 않는 오브젝트들은 transient 리스트에 위치, 자주 사용하는 오브젝트들은 recurrent 리스트에 위치
세션은 shared pool 래치를 획득한 후 버킷을 검색 하여 원하는 크기의 청크를 찾는다면,
해당 청크를 프리 리스트에서 분리한 후, transient 리스트 헤더에 등록 그리고 청크에 핀(pin)을 설정하고,
shared pool 래치를 릴리즈, 만일 적당한 크기의 프리 청크를 못 찾는다면,
더 큰 크기의 청크 검색 하여 필요한 만큼만 자르고 나머지를 프리 리스트에 등록
만일 해당 버킷에서 적절한 청크를 찾지 못한다면,적절한청크를 찾을 때까지 다음 버킷으로 이동하여 청크를 검색 반복
적당한 프리 청크를 찾지 못한다면
LRU 리스트의 꼬리(tail)의 끝으로 이동한 후 몇 개의 청크를 리스트로부터 분리한 후 프리(free)상태로 표시
그리고 이들 청크를 적절한 프리 리스트에 등록, ( 만일 해당 청크가 기존의 프리청크에 인접한 경우라면, 프리 리스트에 등록하기 전에 병합(Coalesce) 수행 )
그런 후에, 적절한 프리청크를 찾기 위해 다시 프리 리스트 버킷을 검색
_shared_pool_reserved_min_alloc 값(기본 설정 4,400bytes)보다 큰 청크 요청 경우
운이 좋은 경우와 동일하게 작동하고, 프리청크를 찾지 못한다면, 예약된 프리 리스트 검색하고, 적절한 프리 청크를 찾지 못하면 LRU 리스트에서 몇 개의 청크를 분리한 후 프리청크를 병합
시나리오 2와 3에서 본 것과 같이 ORA-4031 에러는 프리 리스트에서 적절한 프리청크를 찾지 못하고,
LRU 리스트를 검색하여(오라클 노트에 따르면 5회까지 수행) 기존의 청크를 분리한 후에도 적절한 프리청크를 생성하지 못할 때 발생
예방 : bind 변수 사용, dbms_pool.keep(인스턴스 시작시 라이브러시 캐시에 고정)
*문장 탐색, 실행 비용을 최소화하는 오라클 전략
1. 세션이 KGL pin을 캐시 할 수 있도록 허용 (10.2)
2. KGL pin, lock을 페이지 단위로 할당 (11g)