'version count'에 해당되는 글 1건

  1. 2014.03.23 04 커서공유
ORACLE/ADMIN2014. 3. 23. 22:45
반응형

04 커서공유


(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 같은 클라이언트 애플리케이션에도
리소스를 할당

(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.

  • parse_calls: 라이브러리 캐시에서 SQL 커서를찾으려는요청 횟수
  • loads: 하드파싱을 거친 SQL 실행계획을 라이브러리 캐시에 적재한 횟수
  • executions : SQL을 수행한 횟수
  • invalidations : 커서가 무효화된 횟수. 커서가 참조하고 있는 오브젝트에 중요한 변
    화가일어났음을의미함
#####################################
다른세션 접속후 커서공유 테스트
#####################################

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를발급받게 된다. 즉 커서가 공유되지 않는다.

(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 커서와 공유
못하는지 확인

(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바이트의 청크를 검색.

  • 이과정에서 래치를 획득하지 못하면 latch:shared pool 대기 이벤트를 발생시키며 획득가능할때까지 대기.
    2. 256바이트의 프리청크를 찾았다면 해당 청크를 익스텐트에 할당.
  • 이과정에서 256바이트의 프리청크가 없어서 찾지 못했다면 더큰크기의 프리청크를 검색
    3. 400바이트의 프리청크를 찾았다면 필요한 크기의 256바이트와 144바이트의 크기로 쪼갬.
    256바이트 청크를 익스텐트에 할당. 144바이트의 청크는 프리리스트에 등록
  • 이과정에서 256바이트 보다 더 큰 프리청크를 찾지 못했다면 공유풀 LRU리스트로 부터 핀이 해제된 청크중 256바이트 보다 큰 청크를 검색 후 프리리스트 등록
    4. 공유풀LRU리스트 검색시 필요한 크기의 청크를 확인 못할시 4031에러와 SQL파스 실패

프리리스트에서 프리청크를 검색후 할당받기까지 모든단계에서 shared pool 래치를 할당 받아야 한다.
청크의 할당 해제가 빈번히 발생 한다면 청크는 더욱 작게 쪼개져 관리되어야 할 청크의 수가 증가 한다.
프리리스트를 검색하는 시간을 증가시키고 쉐어드풀 레치에 대한 경합을 증가시켜 성능저하를 유발 하며
4031에러를 발생시킨다.
==================================================================================================================

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 파라미터를 변경 긴급처방

06 바인드 변수의 부작용과 해법

바인드 변수를 사용하면 최초 수행할 때 최적화를 거친 실행계획을 캐시에 적재하고,
실행시점에는 그것을 그대로 가져와 값을 다르게 바인딩하면서 반복 재사용하게 된다.
여기서, 변수를 바인딩하는 시점이 (최적화 시점보다 나중인) 실행시점이라는 사실을 아
는 것이 중요하다. 즉, SQL을 최적화하는 시점에 조건절 컬럼의 데이터 분포도를 활용하
지 못하는 문제점을 갖는다.

(1) 바인드 변수 Peeking

오라클은 9i부터 바인드 변수 Peeking 기능을 도입
SQL이 첫 번째 수행되면서 하드파싱될 때 함께 딸려 온 바인드 변수 값을 살짝 훔쳐 보고
그 값에 대한 컬럼 분포를 이용해 실행계획을 결정
문제점
잘못 수립된 실행계획 때문에 느린 애플리케이션도 문제지만 시스템 운영자 입장에서는
자주 실행계획이 바뀌어 어제와 오늘의 수행속도가 급격히 달라지는 현상

(2) 적응적 커서 공유(Adaptive Cursor Sharing)

오라클 11g도입된 이 기능이 작동하려면 기본적으로 조건절 컬럼에 히스토그램이 생성돼 있어야 한다.
그 상태에서,옵티마이저가 바인드 변수 값에 따라 실행계획을 달리 가져갈 필요가 있다고 판단면
SQL커서에 대해서 이 기능이 활성화
문제점
새로운 실행계획을 만들지 않는다
기존 커서의 실행계획을 그대로 사용해보고 성능이 나쁘다고 판단되면 모드를 전환

(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를 일으키지 않을 뿐 실제실행은 일어나기 때문

(4) 예외적으로,Literal 상수값 사용

게다가 배치 프로그램이나 정보계 시스템에서 수행되는 SQL은 대부분 Long
Running 쿼리이므로 파싱 소요시간이 쿼리 총 소요시간에서 차지하는 비중에 매우 낮
고, 사용빈도도낮아하드파싱에 의한라이브러리 캐시 부하를크게 염려할펼요가없다.

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이 발생할 때마다 라이브러리 캐시를 탐색
하기 전에 세션 커서 캐시를 먼저 살펴본다.

  • users_opening: 공유 커서를 참조하고 있는 세션 커서의 수를 보여준다. 수행을 마
    쳐 커서 를 닫았더라도 참조를 버리지 않은 채 세션 커서 캐시로 옮겨진다면 여기에
    집계된다 .
  • users_executing : 해당 SQL을 현재 실행 중인, 즉 커서가 열려있는 세션 커서의
    수를 보여준다. DML일 때는 수행을 마칠 때 커서가 자동으로 닫히지만 select문은
    EOF(End of Fetch)에 도달했을 때 커서가 닫힌다
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#; 

출처: http://wiki.gurubee.net/pages/viewpage.action?pageId=28115145


반응형
Posted by [PineTree]