h1.04 커서공유
h3.(1) 커서란?
공유 커서 (shared cursor) : 라이브러리 캐시에 공유돼 있는 Shared SQL Area
세션 커서 (session cursor) : Private SQL AREA에 저장된 커서
애플리케이션 커서 (application cursor) : 세션 커서를 가리키는 핸들
그림 7
라이브러리 캐시에 공유돼 있는 커서를 실행할 때는 우선 PGA 영역에 메모리를 할당
Private SQL area 라고 하는데 , Persistent Area 와 Runtime Area로 나뉜다.
Shared SQL Area를 읽어 커서를 실행하는 데 필요한 정보들을 Private SQL Area에 담고,
공유 커서를 가리키는 포인터를 유지한다.그리고 커서의 상태정보도 관리한다.
커서 를 실행하기 위한 이런 준비과정을 "커서를 오픈한다" 고 표현하고,
PGA에 저장된 커서 정보(즉,파싱된 SQL문과 문장을 수행하능데 필요한 기타 정보)를 또
한 '커서' 라고부른다.
PGA에 있는 커서를 핸들링하려면 JAVA, VB, Pro*C, PL!SQL 같은 클라이언트 애플리케이션에도
리소스를 할당
h3.(2) 커서 공유
######################
커서 공유 테스트
######################
-- sys유저 접속
SQL> grant select_catalog_role to scott;
Statement Processed.
SQL> alter system flush shared_pool;
Statement Processed.
-- SCOTT유저 접속
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL> select sql_id, parse_calls, loads, executions, invalidations
, decode(sign(invalidations), 1, (loads-invalidations),0) reloads
from v$sql
where sql_text like '%cursor test%'
and sql_text not like '%v$sql%';
SQL_ID PARSE_CALLS LOADS EXECUTIONS INVALIDATIONS RELOADS
------------- ----------- --------- ---------- ------------- ---------
896pksq7c53f2 1 1 1 0 0
1 rows selected.
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL> select sql_id, parse_calls, loads, executions, invalidations
, decode(sign(invalidations), 1, (loads-invalidations),0) reloads
from v$sql
where sql_text like '%cursor test%'
and sql_text not like '%v$sql%';
SQL_ID PARSE_CALLS LOADS EXECUTIONS INVALIDATIONS RELOADS
------------- ----------- --------- ---------- ------------- ---------
896pksq7c53f2 3 1 3 0 0
1 rows selected.
#####################################
다른세션 접속후 커서공유 테스트
#####################################
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL> select sql_id, parse_calls, loads, executions, invalidations
, decode(sign(invalidations), 1, (loads-invalidations),0) reloads
from v$sql
where sql_text like '%cursor test%'
and sql_text not like '%v$sql%';
SQL_ID PARSE_CALLS LOADS EXECUTIONS INVALIDATIONS RELOADS
------------- ----------- --------- ---------- ------------- ---------
896pksq7c53f2 5 1 5 0 0
1 rows selected.
다른 세션에서 같은 SQL을 수행할 때도 이전 세션에서 적재한 커서를 재사용했음을
알수있다.
###################################
통계 재생성후 커서공유 테스트
###################################
SQL> execute DBMS_STATS.GATHER_TABLE_STATS
(ownname => USER, tabname => 'EMP'
,no_invalidate => FALSE
);
PL/SQL executed.
SQL Execution Time > 00:00:05.367
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL Execution Time > 00:00:00.062
Total Elapsed Time > 00:00:00.062
SQL> select /* cursor test */ empno, ename, job, sal, deptno
from emp
where empno = 7788;
EMPNO ENAME JOB SAL DEPTNO
----- ---------- --------- --------- ------
7788 SCOTT ANALYST 1000 20
1 rows selected.
SQL Execution Time > 00:00:00.062
Total Elapsed Time > 00:00:00.062
-------------------------[Start Time: 2014/03/04 18:39:12]-------------------------
SQL> select sql_id, parse_calls, loads, executions, invalidations
, decode(sign(invalidations), 1, (loads-invalidations),0) reloads
from v$sql
where sql_text like '%cursor test%'
and sql_text not like '%v$sql%';
SQL_ID PARSE_CALLS LOADS EXECUTIONS INVALIDATIONS RELOADS
------------- ----------- --------- ---------- ------------- ---------
896pksq7c53f2 2 2 2 1 1
1 rows selected.
SQL Execution Time > 00:00:00.031
Total Elapsed Time > 00:00:00.047
쿼리를 다시 수행한 후에 v$sql을 다시 조회해 보면, 적재 횟수가 아래처럼 2로 증가한
것을볼수있다.
라이브러리 캐시에 있는 커서틀이 여러 세션에 의해 공유되면서 반복 재사용되는 것
공유된 커서를 사용할 때는 최적화 및 Row-Source Generation 단계를 생략하고
곧바로 실행 단계로 넘어가므로 보다 효율적이고 빠르게 SQL을 수행한다
커서가 공유되려면 커서를 식별하는 키 값이 같아야 함
-> 라이브러리 캐시에서 커서를 식별하기 위해 시용되는 키 값은 'SQL 문장 그 자체'
-> SQL문을 구성하는 전체 문자열이 이름 역할을 한다는 뜻
-> SQL_ID와 SQL FULLTEXT는 1:1로 대응
SQL문 중간에 작은 공백문자 하나만 추가하더라도 서로 다른 SQL 문장으로 인식해 새로운
SQL_ID를발급받게 된다. 즉 커서가 공유되지 않는다.
h3.(3) Child 커서를 공유하지 못하는 경우
8QL마다 하나의 Parent 커서를 가지며, Child 커서는 여러 개일 수 있다.
실제 수행에 필요한 정보는 Child 커서에 담기므로 적어도 한 개의 Child 커서를 갖는다.
v$sqlarea는 Parent 커서 정보를 보여주고36) ' v$sql은 Child 커서 정보를 보여준다.
아래는 SC0TT과 HR 스키마 각각에 EMP 테이블을 만들고 각 계정으로 로그인 해서
select * from emp 쿼리를 수행한 후에 v$sqlarea와 v$sql을 쿼리했을 때의 결과를 보
이고 있다.
##########################
자식커서의 공유 테스트
##########################
C:\WINDOWS\system32>sqlplus "/as sysdba"
SQL> conn /as sysdba
연결되었습니다.
SQL> create table hr.emp as select * from scott.emp;
테이블이 생성되었습니다.
SQL> alter system flush shared_pool;
시스템이 변경되었습니다.
SQL> conn scott/tiger
연결되었습니다.
SQL> select * from emp where empno=7698;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7698 BLAKE MANAGER 7839 81/05/01 2850 30
SQL> conn hr/hr
연결되었습니다.
SQL> select * from emp where empno=7698;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7698 BLAKE MANAGER 7839 81/05/01 2850 30
== v$sqlarea는 부모커서를 확인
SQL> conn /as sysdba
연결되었습니다.
SQL> select sql_id, version_count, optimizer_mode, address, hash_value
2 from v$sqlarea
3 where sql_text like 'select * from emp where%'
4 and sql_text not like 'v$sql';
SQL_ID VERSION_COUNT OPTIMIZER_ ADDRESS HASH_VALUE
------------- ------------- ---------- -------- ----------
5tq0u8y4zd3ta 2 ALL_ROWS 3D9C5BFC 2314637098
== v$sql는 자식커서를 확인
SQL> select sql_id, child_number, optimizer_mode, address, hash_value, parsing_user_id
2 from v$sql
3 where sql_text like 'select * from emp where%'
4 and sql_text not like 'v$sql';
SQL_ID CHILD_NUMBER OPTIMIZER_ ADDRESS HASH_VALUE PARSING_USER_ID
------------- ------------ ---------- -------- ---------- ---------------
5tq0u8y4zd3ta 0 ALL_ROWS 3D9C5BFC 2314637098 84
5tq0u8y4zd3ta 1 ALL_ROWS 3D9C5BFC 2314637098 85
== v$sqlarea 에 동일문장의 버전이 몇개인지 볼수 있다.
select * from v$sqlarea order by version_count desc;
하나의 SQL문장이 여러개 Child 커서를 갖게 되는 대표적인 이유
1. SQL에서 참조하는 오브젝트명이 같지만 SQL을 실행한 사용자에 따라 다른 오브젝트를 가리킬때
2. 참조 오브젝트가 변경돼 커서가 무효화되면 이후 그 커서를 처음 사용하려는 세션에
의해 다시 하드파싱돼야 하는데 특정 세션이 아직 기존 커서를 사용 중(Pin)일 때
3. 옵티마이저 모드를 비롯해 옵티마이저 관련 파라미터가 다를 때
4. 입력된 바인드 값의 길이가 크게 다를 때
5. NLS 파라미터를 다르게 설정했을 때
6. SQL 트레이스를활성화했을때
해당 딕셔너리를 참조해라. ====> V$SQL_SHARED_CURSOR
SQL> conn scott/tiger
연결되었습니다.
SQL> conn /as sysdba
연결되었습니다.
SQL> alter system flush shared_pool;
시스템이 변경되었습니다.
SQL> conn scott/tiger
연결되었습니다.
SQL> alter session set optimizer_mode=first_rows;
세션이 변경되었습니다.
SQL> select * from emp where empno=7698;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7698 BLAKE MANAGER 7839 81/05/01 2850 30
SQL> alter session set optimizer_mode=all_rows;
세션이 변경되었습니다.
SQL> select * from emp where empno=7698;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7698 BLAKE MANAGER 7839 81/05/01 2850 30
SQL> conn /as sysdba
연결되었습니다.
SQL> select sql_id, version_count, optimizer_mode, address, hash_value
2 from v$sqlarea
3 where sql_text like 'select * from emp where%'
4 and sql_text not like 'v$sql';
SQL_ID VERSION_COUNT OPTIMIZER_ ADDRESS HASH_VALUE
------------- ------------- ---------- -------- ----------
5tq0u8y4zd3ta 2 ALL_ROWS 3D9C5BFC 2314637098
SQL> select child_number, child_address, optimizer_mode_mismatch, optimizer_mismatch
2 from V$SQL_SHARED_CURSOR
3 where SQL_ID = '5tq0u8y4zd3ta'
4 and ADDRESS = '3D9C5BFC';
CHILD_NUMBER CHILD_AD O O
------------ -------- - -
0 37DF4684 N N
1 37FF5A94 Y N
SQL> select *
2 from V$SQL_SHARED_CURSOR
3 where SQL_ID = '5tq0u8y4zd3ta'
4 and ADDRESS = '3D9C5BFC';
SQL_ID ADDRESS CHILD_AD CHILD_NUMBER U S O O S L F E B P I S T A B D L T B I I R L I O E M U
T N F A I T D L D B P C S C P T M B M R O P M F L P L A F L R L H P B
------------- -------- -------- ------------ - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5tq0u8y4zd3ta 3D9C5BFC 37DF4684 0 N N N N N N N N N N N N N N N N N N N N N N N N N N N N
N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N
5tq0u8y4zd3ta 3D9C5BFC 37FF5A94 1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N
N N N N N N N N N N N N N N N N N N N N Y N N N N N N N N N N N N N N
Version Count 수치가 높은 SQL 일수록 커서를 탐색하는 데 더 많은 시간을 소비하므
로 library cache 래치에 대한 경합 발생 가능성을 증가시킨다.
v$sql_shared_cursor 다이나믹 뷰는 새로운 Child 커서가 왜 기존 Child 커서와 공유
못하는지 확인
h3.(4) Parent 커서를 공유하지 못하는 경우
1. 공백 문자 또는 줄바꿈
SELECT * FROM CUSTOMER;
SELECT * FROM CUSTOMER;
2 . 대소문자구분
SELECT * FROM CUSTOMER;
SELECT * FROM Customer;
3. 태이블 Owner 명시
SELECT * FROM CUSTOMER;
SELECT * FROM HR.CUSTOMER;
4. 주석 (Comment)
SELECT * FROM CUSTOMER;
SELECT /* 주석문 */ * FROM CUSTOMER;
5. 용티마이져 힌트 사용
SELECT * FROM CUSTOMER;
SELECT /*+ all_rows */ * FROM CUSTOMER;
6. 조건 절 비교값
SELECT * FROM CUSTOMER WHERE CUST_ID = '0000001';
SELECT * FROM CUSTOMER WHERE CUST_ID = '0000002';
이 외에도 더 다양한 케이스가 있을 것이다. 1. 2. 4번은 그 실행계획이 100% 같다.
그럼에도 문자열을 조금 다르게 기술했다는 이유 때문에 서로 다른 SQL로서 각각
하드파싱을 일으카고 서로 다른 공간을 차지하면서 Shared Pool을 낭비하게 된다. 이런
비효율을 줄이고 공유 가능한 형태로 SQL을 작성하려면 개발 초기에 SQL 작성 표준을
정해 이를 준수하도록 해야 한다.
5번은 의도적으로 실행계획을 달리 가져가려는 것이므로 논외로 하고 라이브러리 캐시
효율과 직접 관련이 큰 것은 6번 같은 패턴이다. 즉 조건절에 바인드 변수를 사용하지 않
고 서로 다른 Literal 값으로 문자열을 대체하는 경우다.
만약 OLTP성 업무를 처리하는 애플리케이션에서 6번과 같은 패턴으로 SQL을 개발한
다변 결코 좋은 성능을 보장받을 수 없다. 개별 쿼리 성능으로 보면 잘 느끼지 못하지만
동시 트랜잭션이 몰리는 peak 시간대에 시스템을 장애 상황으로 몰고 가는 주범이다.
==================================================================================================================
공유풀은 힙(heap)으로 불리는 OS메모리 공간으로 구성됨.
힙(heap)은 헤더와 하나이상의 메모리익스텐트로으로 구성됨.
메모리익스텐트는 지속적으로 할당과 반납이 반복되며 여러개 작은조각으로 나누어짐.
메모리 영역이 작은단위로 나누어 지는 것을 공유풀단편화(FRAGMENTATION)이라 함.
단편화로 인해 쪼개진 영역은 청크라고 불리며 프리리스트에 의해 관리됨.
한번 사용된 청크는 다시 프리리스트에 등록되기 전까지 공유풀 LRU리스트에서 관리됨
LRU알고리즘을 이용하여 청크의 재사용률을 높이기 위해서임.
공유풀에 메모리 할당과정
1. 새로운 SQL에 대해 파스 수행하려면 힙영역 새로운 빈공간 할당받아야함.
2. 프리리스트에서 필요한 크기의 프리청크 서칭
3. 프리리스트 서칭후 있으면 힙영역 할당. 없으면 LRU리스트 사용가능 청크를 서칭
4. LRU리스트에도 없으면 4031에러 발생후 SQL파스 실패
예> SQL파스를 위해 256바이트가 필요할떄
1. 쉐어드풀 래치획득후 프리리스트로 부터 256바이트의 청크를 검색.
프리리스트에서 프리청크를 검색후 할당받기까지 모든단계에서 shared pool 래치를 할당 받아야 한다.
청크의 할당 해제가 빈번히 발생 한다면 청크는 더욱 작게 쪼개져 관리되어야 할 청크의 수가 증가 한다.
프리리스트를 검색하는 시간을 증가시키고 쉐어드풀 레치에 대한 경합을 증가시켜 성능저하를 유발 하며
4031에러를 발생시킨다.
==================================================================================================================
h1.05 바인드 변수의 중요성
바인드 변수 사용에 따른 효과는 아주 분명
커서를 많이 생성하지 않고 하나를 반복 재사용하므로 메모리 사용량과 파싱 소요 시간을 불인다
궁극적으로 시스템전반의 메모리와 CPU사용률을 낮춰 데이터베이스 성능과 확장성을 높이는데 기여
특히 동시 사용자 접속이 많을 때는 그 영향력이 절대적이다
#################################
바인드 변수 사용의 필요성 테스트
#################################
SQL> create table t as select * from dba_objects;
테이블이 생성되었습니다.
SQL> update t set object_id = rownum;
72471 행이 갱신되었습니다.
SQL> commit;
커밋이 완료되었습니다.
SQL> create unique index t_idx on t (object_id);
인덱스가 생성되었습니다.
SQL> analyze table t compute statistics;
테이블이 분석되었습니다.
set timing on
declare
type rc is ref cursor;
l_rc rc;
l_object_name t.object_name%type;
begin
for i in 1..20000
loop
open l_rc for
'select /* TEST1 */ object_name
from t
where object_id = :x' using i;
fetch l_rc into l_object_name;
close l_rc;
end loop;
end;
/
SQL> set timing on
SQL> declare
2 type rc is ref cursor;
3 l_rc rc;
4 l_object_name t.object_name%type;
5 begin
6 for i in 1..20000
7 loop
8 open l_rc for
9 'select /* TEST1 */ object_name
10 from t
11 where object_id = :x' using i;
12 fetch l_rc into l_object_name;
13 close l_rc;
14 end loop;
15 end;
16 /
PL/SQL 처리가 정상적으로 완료되었습니다.
경 과: 00:00:00.79
SQL>
SQL> col sql_text format a50
SQL> select sql_text,loads,parse_calls,executions,fetches
2 from v$sql
3 where sql_text like '%TEST1%'
4 and sql_text not like '%v$sql%'
5 and sql_text not like 'declare%';
SQL_TEXT LOADS PARSE_CALLS EXECUTIONS FETCHES
-------------------------------------------------- ---------- ----------- ---------- ----------
select /* TEST1 */ object_name from t wh 1 20000 20000 20000
ere object_id = :x
경 과: 00:00:00.03
declare
type rc is ref cursor;
l_rc rc;
l_object_name t.object_name%type;
begin
for i in 1..20000
loop
open l_rc for
'select /* TEST2 */ object_name
from t
where object_id = '||i ;
fetch l_rc into l_object_name;
close l_rc;
end loop;
end;
/
SQL> declare
2 type rc is ref cursor;
3 l_rc rc;
4 l_object_name t.object_name%type;
5 begin
6 for i in 1..20000
7 loop
8 open l_rc for
9 'select /* TEST2 */ object_name
10 from t
11 where object_id = '||i ;
12 fetch l_rc into l_object_name;
13 close l_rc;
14 end loop;
15 end;
16 /
PL/SQL 처리가 정상적으로 완료되었습니다.
경 과: 00:00:07.54
SQL>
select sql_text,loads,parse_calls,executions,fetches
from v$sql
where sql_text like '%TEST2%'
and sql_text not like '%v$sql%'
and sql_text not like 'declare%';
select /* TEST2 */ object_name from t wh 1 1 1 1
ere object_id = 19110
select /* TEST2 */ object_name from t wh 1 1 1 1
ere object_id = 18922
select /* TEST2 */ object_name from t wh 1 1 1 1
ere object_id = 18476
select /* TEST2 */ object_name from t wh 1 1 1 1
ere object_id = 19948
select /* TEST2 */ object_name from t wh 1 1 1 1
ere object_id = 19051
2091 개의 행이 선택되었습니다.
경 과: 00:00:01.25
바인드 변수 사용원칙을 잘 지커지 않으면 라이브러리캐시 경합때문에 시스템 정상가동이
어려운 상황에 직면할 수 있다. cursor_sharing 파라미터를 변경 긴급처방
h1.06 바인드 변수의 부작용과 해법
바인드 변수를 사용하면 최초 수행할 때 최적화를 거친 실행계획을 캐시에 적재하고,
실행시점에는 그것을 그대로 가져와 값을 다르게 바인딩하면서 반복 재사용하게 된다.
여기서, 변수를 바인딩하는 시점이 (최적화 시점보다 나중인) 실행시점이라는 사실을 아
는 것이 중요하다. 즉, SQL을 최적화하는 시점에 조건절 컬럼의 데이터 분포도를 활용하
지 못하는 문제점을 갖는다.
h3.(1) 바인드 변수 Peeking
오라클은 9i부터 바인드 변수 Peeking 기능을 도입
SQL이 첫 번째 수행되면서 하드파싱될 때 함께 딸려 온 바인드 변수 값을 살짝 훔쳐 보고
그 값에 대한 컬럼 분포를 이용해 실행계획을 결정
문제점
잘못 수립된 실행계획 때문에 느린 애플리케이션도 문제지만 시스템 운영자 입장에서는
자주 실행계획이 바뀌어 어제와 오늘의 수행속도가 급격히 달라지는 현상
h3.(2) 적응적 커서 공유(Adaptive Cursor Sharing)
오라클 11g도입된 이 기능이 작동하려면 기본적으로 조건절 컬럼에 히스토그램이 생성돼 있어야 한다.
그 상태에서,옵티마이저가 바인드 변수 값에 따라 실행계획을 달리 가져갈 필요가 있다고 판단면
SQL커서에 대해서 이 기능이 활성화
문제점
새로운 실행계획을 만들지 않는다
기존 커서의 실행계획을 그대로 사용해보고 성능이 나쁘다고 판단되면 모드를 전환
h3.(3) 입력 값에 따라 SQL 분리
인텍스 액세스 경로(access Path)로서 중요하고 조건절 컬럼의 데이터 분포가 균일하지 않
은 상황에서 바인드 변수 시용에 따른 부작용을 피하려면 바인딩 되는 값에 따라 실행계
획을 아래와 같이 분리하는 방안을 고려해야 한다.
select /*+ full(a) */ *
from 아파트매물 a
where :city in ('서울','경기')
and 도시 = :city
union all
select /*+ index(a) */ *
from 아파트매물 a
where :city not in ('서울','경기')
and 도시 = :city;
문제점
union all을 이용해 SQL을 길게 작성하면 Parse 단계의 CPU 사용률을 높임.
-라이브러리 캐시는 문장을 저장함
-Syntax를 체크함
-파싱트리를 만들어 Semantic 체크도함
union all을 이용해 SQL을 길게 작성하면 Execute단계에서도 CPU 사용률을 높임.
-I/O를 일으키지 않을 뿐 실제실행은 일어나기 때문
h3.(4) 예외적으로,Literal 상수값 사용
게다가 배치 프로그램이나 정보계 시스템에서 수행되는 SQL은 대부분 Long
Running 쿼리이므로 파싱 소요시간이 쿼리 총 소요시간에서 차지하는 비중에 매우 낮
고, 사용빈도도낮아하드파싱에 의한라이브러리 캐시 부하를크게 염려할펼요가없다.
h1.07 세션 커서 캐싱
세션 커서 (session cursor)란
Shared Pool에 위치한 공유 커서 (shared cursor)를 실행하려고 PGA로 인스턴스화한 것
쿼리를 수행한 후에 커서를 닫으면,
세션 커서를 위해 할당된 메모리는 물론 공유 커서를 가리키는 포인터까지바로 해제,
그 다음에 같은 SQL을 수행하면 커서를 오픈하기 위한 라이브러리 캐시 탐색작업을 다시 해야 함.
세션 커서 캐싱(Session Cursor Cache)란
오라클은 자주 수행하는 SQL에 대한 세션 커서를 세션 커서 캐시 (Session cursor Cache)에 저장
커서를 닫는 순간 해당 커서의 Parse Call 횟수를 확인해 보고 그 값이 3보다 크거나 같으면,
세션 커서를 세션 커서 캐시로 옮긴다. 세션 커서 캐시에는 SQL 텍스트와 함께 공유 커서를
가리키는 포인터를 저장
커서는 닫힌 상태지만 공유 커서에 대한 참조를 유지하기 때문에 다음 수행 시 더 빨리
커서를 오픈(자주 수행되는 SQL문에 의해 발생하는 라이브러리 캐시 부히를 경감)
즉,
SQL문을 파싱해서 구문을 분석하고,
라이브러리 캐시에서 커서를 찾는 과정에서 소모되는 CPU 사용량을 줄일 수 있음은 물론,
소프트 파싱 과정에 발생하는 래치 요청 횟수를 감소시키는 효과
세션 커서 캐시 내에서도 LRU 알고리즘을 시용함으로써 새로운 엔트리를 위한 공간이 필요할 때마다
기존 세션 커서 중 사용 빈도가 낮은것부터 밀어낸다.
session_cached_cursors는 얼마나 많은 세션 커서를 캐싱할지를 지정하는 파라미터
로서 , 이 값을 O보다 크게 설정하면 Parse Call이 발생할 때마다 라이브러리 캐시를 탐색
하기 전에 세션 커서 캐시를 먼저 살펴본다.
SQL> alter session set session_cached_cursors = 10;
세션이 변경되었습니다.
SQL> alter system flush shared_pool;
시스템이 변경되었습니다.
SQL> select * from scott.emp where empno=7788 ;
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- -------- ---------- ----------
DEPTNO
----------
7788 SCOTT ANALYST 7566 87/04/19 3000
20
SQL> select parse_calls, users_opening, users_executing from v$sql
2 where sql_text = 'select * from scott.emp where empno=7788 ';
PARSE_CALLS USERS_OPENING USERS_EXECUTING
----------- ------------- ---------------
1 0 0
SQL> select * from scott.emp where empno=7788 ;
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- -------- ---------- ----------
DEPTNO
----------
7788 SCOTT ANALYST 7566 87/04/19 3000
20
SQL> select parse_calls, users_opening, users_executing from v$sql
2 where sql_text = 'select * from scott.emp where empno=7788 ';
PARSE_CALLS USERS_OPENING USERS_EXECUTING
----------- ------------- ---------------
2 0 0
SQL> select * from scott.emp where empno=7788 ;
EMPNO ENAME JOB MGR HIREDATE SAL COMM
---------- ---------- --------- ---------- -------- ---------- ----------
DEPTNO
----------
7788 SCOTT ANALYST 7566 87/04/19 3000
20
SQL> select parse_calls, users_opening, users_executing from v$sql
2 where sql_text = 'select * from scott.emp where empno=7788 ';
PARSE_CALLS USERS_OPENING USERS_EXECUTING
----------- ------------- ---------------
3 1 0
세번 일어나는 순간 users_opening값이 0에서 1로 바뀐 것 확인
세번째 수행되기 전까지는 users_opening값이 0인것
커서를 닫자마자 공유커서에 대한 참조까지 곧바로 해제
users_opening에서 'open' 의 의미가 실제 커서가 열려 있음을 의미하는 것이 아님을
기억할 필요가 있다. 커서는 닫힌 상태지만 공유 커서에 대한 참조를 유지하기 때문에 다
음수행 시 더 빨리 커서를오픈할수있는것이다.
create table t(x number);
select a.name, b.value
from v$statname a, v$mystat b
where a.name in ('session cursor cache hits','parse count (total)')
and b.statistic# = a.statistic#;
alter session set session_cached_cursors=0;
declare
i number;
begin
for i in 1..10000
loop
execute immediate 'insert into t values('||mod(i,100)||')';
end loop;
commit;
end;
/
select a.name, b.value
from v$statname a, v$mystat b
where a.name in ('session cursor cache hits','parse count (total)')
and b.statistic# = a.statistic#;
alter session set session_cached_cursors=100;
declare
i number;
begin
for i in 1..10000
loop
execute immediate 'insert into t values('||mod(i,100)||')';
end loop;
commit;
end;
/
select a.name, b.value
from v$statname a, v$mystat b
where a.name in ('session cursor cache hits','parse count (total)')
and b.statistic# = a.statistic#;