아래 blog 내용 처럼 v$lock을 조회할때 그리 시간이 많이 걸리는 경험은 없었던것 같지만..
v$lock을 조회하면 내부 fixed table join시 'MERGE JOIN CARTESIAN'이 사용되며 여기에 비용이 많이 든다는 군요.
혹시 v$lock query 결과가 늦으시면 ordered hint를 써보심이...


SQL statement for V$LOCK!!! 
select s.inst_id,l.laddr,l.kaddr,s.ksusenum,r.ksqrsidt,r.ksqrsid1, r.ksqrsid2,l.lmode,l.request,l.ctime,decode(l.lmode,0,0,l.block) from v$_lock l, x$ksuse s, x$ksqrs r where l.saddr=s.addr and concat(USERENV('Instance'),l.raddr)=concat(r.inst_id,r.addr) and s.inst_id = USERENV('Instance'); 




oracle index rebuild를 판단하는 기준이나 rebuild가 필요한지 자체에 대한 많은 말들이 있긴한데.. 

다음의 글은 index_stats view의 del_lf_rows를 이용한 index rebuild 방안에 대한 구조적인 한계에 대한 설명입니다.
뭐 이러한 방안을 대량 delete 작업 후 바로 적용하면 될 거 같은데,
아래와 같은 내용을 알아둬야 .. 수치가 좀 이상하게 나올 경우 당황하지 않겠네요~ 

But, as the Oracle myth busters like Richard Foote have been saying for years,  that's not how Oracle's B-tree indexes work. When you delete an index entry, Oracle marks it as deleted but leaves it in place. When you commit your transaction Oracle does nothing to the index entry – but other processes now know that the entry can be wiped from the block allowing the space to be re-used.      (원문 : Index Rebuilds « Oracle Scratchpad )




HOT backup mode에서 왜 redo log에 추가 정보가 쓰일까요? 
ALTER DATABASE BEGIN BACKUP과 ALTER TABLESPACE BEGIN BACKUP의 차이는? 

아래 두개의 POST에 모든 답이 있네요.. 

Why is excessive redo generated during an Online/Hot Backup

There is not excessive redo generated, there is additional information logged into the online redo log during a hot backup the first time a block is modified in a tablespace that is in hot backup mode.  

in hot backup mode only 2 things are different:

o the first time a block is changed in a datafile that is in hot backup mode, the ENTIRE BLOCK is written to the redo log files, not just the changed bytes.  Normally only the changed bytes (a redo vector) is written. In hot backup mode, the entire block is logged the FIRST TIME.  This is because you can get into a situation where the process copying the datafile and DBWR are working on the same block simultaneously.  Lets say they are and the OS blocking read factor is 512bytes (the OS reads 512 bytes from disk at a time).

The backup program goes to read an 8k Oracle block.  The OS gives it 4k.  Meanwhile -- DBWR has asked to rewrite this block.  the OS schedules the DBWR write to occur right now.  The entire 8k block is rewritten.  The backup program starts running again (multi-tasking OS here) and reads the last 4k of the block.  The backup program has now gotten an impossible block -- the head and tail are from two points in time.  We cannot deal with that during recovery.  Hence, we log the entire block image so that during recovery, this block is totally rewritten from redo and is consistent with itself at least.  We can recover it from there.

o the datafile headers which contain the SCN of the last completed checkpoint are NOT updated while a file is in hot backup mode.  This lets the recovery process understand what archive redo log files might be needed to fully recover this file.

Why is excessive redo generated during an Online/Hot Backup

What about ALTER DATABASE BEGIN BACKUP ?
That command put all database tablespaces in backup mode at the same time. As seen previously, it is a bad idea to put all tablespaces in backup mode, as it is better to do it one by one in order to minimize the supplemental redo logging overhead. Oracle introduces this ‘shortcut’ for one reason only: when doing backup with a mirror split (BCV, Flashcopy, etc), the copy gets all the datafiles at the same time, and the copy lasts only few seconds. In that case, it is easier to use that command to put all tablespaces in backup mode during the operation.

http://narashimreddy.wordpress.com/2009/09/11/oracle-backup-and-recovery-description-of-begin-backup-end-backup/

library cache lock/pin은 library cache내의 object에 대한 매우 빠른 시간내에 처리되는 lock 메카니즘입니다.

따라서 일반적인 운영상황에서는 library cache lock/pin 등의 contention을 dump 등으로 정보를 수집하기는 엄청어렵죠.
뭐 운영중 DDL을 수행하면 library cache lock/pin을 유발할 순 있겠지만..

다음의 시나리오는 2006년 가을호 오라클 매거진에 있는 hang을 유발하는 시나리오 입니다.
(보고 싶은신 분들은 www.oracle.com에 가면 아마~ 있을 겁니다.)
오라클 매거진에서는 system state dump를 설명하고 있지만, 
요 시나리오를 이용해서 library cache dump를 떠보겠습니다. 

욱짜님의 오라클 블로그에 보면 "library cache pin self deadlock 만들기" 방법도 있습니다..

* 준비 과정

create table employee (empno number,ename varchar2(200), deptno number);
insert into employee values (1,'Alice',10);
commit;
create or replace procedure deleteEmployee(eno number)
is
begin
delete employee where empno = eno;
end;
/

* 시나리오
T1 (SID 110) : delete employee where empno = 1;
T2 (SID 92)  : exec deleteEmployee(1);
T3 (SID 67)  : alter procedure deleteEmployee compile;
T4 (SID 36)  : exec deleteEmployee(2);

alter session set events 'immediate trace name LIBRARY_CACHE level 11';


위와 같이 수행하게 되면 SID 92, 7, 36은 sql prompt가 떨어지지 않고 기다리게 됩니다.
각각 어떤 resource를 기다리고 있을까요?

* wait event
       SID EVENT                                  P1 P1RAW                    P2 P2RAW                    P3
---------- ------------------------------ ---------- ---------------- ---------- ---------------- ----------
        36 library cache lock             4.4044E+12 000004017D44D868 4.4040E+12 0000040163A70D88        201
        67 library cache pin              4.4044E+12 000004017D44D868 4.4040E+12 0000040163D23718        301
        92 enq: TX - row lock contention  1415053318 0000000054580006     131076 0000000000020004      31074
       110 SQL*Net message from client    1650815232 0000000062657100          1 0000000000000001          0

* lock info

ADDR             KADDR                   SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
---------------- ---------------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
000004017E512348 000004017E512368         92 TX     131076      31074          0          6        144          0
000004017BBA0DC0 000004017BBA0DE8        110 TM     107693          0          3          0        150          0
000004017BBA0EC0 000004017BBA0EE8         92 TM     107693          0          3          0        144          0
000004017BC45998 000004017BC459D0        110 TX     131076      31074          6          0        150          1

wait event를 보면

SID 110이 "delete employee where empno = 1"을 수행해서 employee table에 대한 TM lock과 empno가 1인 row에 대한 TX lock을 획득하고 있습니다. 물론 commit을 하지 않아 그 상태가 계속 유지가 되죠.

SID 92는 "deleteEmployee(1)"을 수행해 empno가 1인 row를 지우려 하지만 이미 해당 row에 대한 lock을 SID 110이 갖고 있어 이를 기다리게 됩니다. 이 상태에서 해당 procedure는 현재 수행 중이기 때문에 library cache pin을 갖고 있게 됩니다.

SID 67은 deleteEmployee procedure를 수정하려 하기 때문에 관련 library cache lock과 pin이 필요하게 됩니다. 그러나 library cache pin은 현재 SID 92에서 갖고 있기 때문에 이를 기다려야 합니다.

SID 36은 "deleteEmployee(2)"를 수행하려 합니다. 그러나 deleteEmployee procedure를 수행하기 위해서는 library cache lock에 대한sahred lock을 할당 받아야 합니다.

우선 wait event에 있는 library cache handle 먼저 찾아 보겠습니다. (library cache lock/pin event는 p1 값이 library cache handle을, p2 값이 각각 library cache lock/pin address를 가리킵니다.)

아래 그림을 보시면 SCOTT.DELETEEMPLOYEE library cache object의 lock과 pin의 holder, waiter 정보를 확인할 수 있습니다. 그리고 DEPENDENCIES 항목을 보면 library cache object의 관련 object들도 list-up 된 것을 확인 할 수 있습니다.



library cache dump 등을 장애때 직접 수행해서 관련 정보를 분석할 일은 별로 없긴 하지만,
이를 통해 조금 더 오라클 내부 구조나 hang/lock 현상 등을 좀더 잘 이해 할 수 있는 과정이 될 수 있지 않을까 생각합니다.





We promised in an earlier post to cover some of the questions from the Optimizer round table discussion at Oracle Open World. Here's our first in a series of posts that will address these questions. 

Q: When moving from 10g to 11g, should hints in existing SQL be removed? 

A: I was glad to see this question at the round table, since I think this is something people often overlook when adding hints to an application. Over time, hints can become stale. You may have added a hint to a query because of a weakness in an old version of the optimizer, but the hint might no longer be necessary to get the best plan. Even worse, the hint may force a plan which is suboptimal, for a couple of reasons:

이 질문은 오라클 데이터베이스 관계자라면 매우 자주 듣는 질문이죠. 
이전 버전의 SQL에 적용된 hint를 제거하는게 맞을까요? 아님 계속 사용하는게 맞을까요? 
물론 답은 당연히 제거하라 겠죠? 

새로운 버전에서, 그리고 변경되는 데이터 상에서 해당 hint가 적정할지,
또 hint를 사용한 사람이 선택한 plan이 과연 적정할지.. 
그 hint는 새로운 버전에서 계속 존재하는지..
이러한 판단이 가능한 사람이 있을까 모르겠네요.. 

위의 포스트는 오라클 optimizer 팀 blog의 post 입니다. open world에서 질문 나온걸 정리한다네요. 
한번 읽어 보세요 ~
baseline을 이용한 plan 고정하는 방법도 나오네요..

그나저나 _optimizer_ignore_hints parameter는 언제 생긴걸까? 
이거 쓸만할 거 같은데, 확인 좀 해 봐야겠네요~


* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!



보통, Test 시스템에서 가동 시스템으로 데이타를 이관하는 경우, 데이타 뿐만 아니라 통계정보도 이관하여, Test계와 가동계를 동일하게 유지하고자 하는 경우가 있읍니다. 이러한 경우,데이타는 쉽게 옮겨도 통계치는 딱히 옮기는 방법이 없습니다. 이때 아래의 방법을 사용하시면 쉽게 올길수 있읍니다.

dbms_stats.export_table_stats(
ownname   VARCHAR2,                -- schema name
tabname   VARCHAR2,                -- table name
partname  VARCHAR2 DEFAULT NULL,   -- partition name
stattab   VARCHAR2,                -- stat table name
statid    VARCHAR2 DEFAULT NULL,   -- optional identifier
cascade   BOOLEAN  DEFAULT TRUE,   -- TRUE = indexes too
statown   VARCHAR2  DEFAULT NULL); -- stat table schema

exec dbms_stats.export_table_stats(USER, 'servers', NULL, 'STAT_TAB', NULL, TRUE);

dbms_stats.import_table_stats(
ownname       VARCHAR2,
tabname       VARCHAR2,
partname      VARCHAR2 DEFAULT NULL,
stattab       VARCHAR2,
statid        VARCHAR2 DEFAULT NULL,
cascade       BOOLEAN  DEFAULT TRUE,
statown       VARCHAR2 DEFAULT NULL,
no_invalidate BOOLEAN  DEFAULT to_no_invalidate_type(get_param('NO_INVALIDATE')),
force         BOOLEAN DEFAULT FALSE);

exec dbms_stats.import_table_stats(USER, 'servers', stattab=>'STAT_TAB');

예 : SCOTT의 EMP  통계를 SCOTT2의 EMP2로 이관

1. 통계정보를 임시로 저장할 Table생성.
SQL> exec dbms_stats.create_stat_table('SCOTT','STATS');

2. 원하는 table의 통계정보를 임시 table로 이관.
SQL> exec dbms_stats.export_table_stats('SCOTT','EMP',NULL,'STATS','emp stats',TRUE);

* 참고 (updated: 2010/08/03)
"emp stats"로 중간에 space가 들어가니 에러가 나는 군요.. 이래서 직접 테스트를해봐야 한다니까...

SQL> exec dbms_stats.export_table_stats('SCOTT','EMP',NULL,'STATS','EMP_STATS',TRUE,'SCOTT');

BEGIN dbms_stats.export_table_stats('SCOTT','EMP',NULL,'STATS','EMP STATS',TRUE,'SCOTT'); END;
*
ERROR at line 1:
ORA-20001: EMP STATS is an invalid identifier
ORA-06512: at "SYS.DBMS_STATS", line 8240
ORA-06512: at "SYS.DBMS_STATS", line 9188
ORA-06512: at line 1

* 주의 (updated: 2010/08/03)
STATS table의 데이터를 qery 해보면 EMP table의 원래 소유자인 SCOTT이 C5 column에 명시되어 있습니다. 만약 다른 계정으로, 즉 SCOTT2 계정으로 통계정보를 옮기려면 STATS table의 SCOTT값을 SCOTT2로 update 해주어야 합니다. 
두개의 DB의 계정이 동일하다면 변경해 줄 필요는 없겠죠~


3. 임시 table을 export, 그리고 target db로 import.
 $exp scott/tiger tables=STATS file=expstat.dmp
 $imp scott/tiger file=expstat.dmp full=y log=implog.txt
(만약 동일 DB라면 이 과정은 생략 가능합니다.)

4. 임시 Table로 부터 통계치를 원하는 table에 넣는다.
SQL> exec dbms_stats.import_table_stats('SCOTT2','EMP2',NULL,'STATS');





CROSS JOIN/ Cartesian Products

 - Cartesian product 값을 얻을때 사용 합니다. 
 - 즉 join을 통해 생성가능한 모든 row를 return 합니다. 

SQL>SELECT ename FROM emp CROSS JOIN dept;
SQL>SELECT ename FROM emp, dept;

참고: CROSS가 생략되어 INNER join으로 수행되는 경우(join condition이 없는 경우) 나 CROSS join에서 join condition이 기술된 경우 에러발생합니다. 

SQL>SELECT e.lname, d.name FROM employee e JOIN department d;
ORA-00905: missing keyword

SQL>SELECT e.lname, d.name FROM employee e CROSS JOIN department d ON e.dept_id = d.dept_id;
ORA-00933: SQL command not properly ended


INNER JOIN 

 - 일반 조인시 Table간의 ','를 생략하고 'INNER JOIN'을 추가하고 WHERE절대신 ON절을 사용해야 합니다.
 - INNER는 생략 가능합니다.

SQL>SELECT e.empno, e.ename FROM dept d INNER JOIN emp e ON d.deptno=e.deptno;
SQL>SELECT e.empno, e.ename FROM dept d , emp e WHERE d.deptno=e.deptno;

EQUI-JOIN/ NON-EQUI-JOIN 
 - Join condition에 equal operation(=)을 사용하는 JOIN 
 - Join condition에 equal operation 외의 다른 operation을 사용하는 JOIN

SQL> SELECT s.name supplier_name, p.name part_name FROM supplier s JOIN part p ON s.supplier_id = p.supplier_id;
SQL> SELECT p.name part_name, c.inv_class inv_class FROM part p JOIN inventory_class c ON p.unit_cost BETWEEN c.low_cost AND c.high_cost;

SELF JOIN
 - 동일 TABLE에 대한 JOIN

SQL> SELECT e.lname employee, m.lname manager FROM employee e JOIN employee m ON e.manager_emp_id = m.emp_id;

OUTER JOIN 

 - Join시 특정 table의 join 조건에 일치하지 않은 row를 추가할때 사용해야 합니다.
 - { LEFT | RIGHT | FULL } [OUTER]

SQL>SELECT e.empno, e.ename FROM dept d LEFT OUTER JOIN emp e ON d.deptno=e.deptno;
SQL>SELECT e.empno, e.ename FROM dept d , emp e WHERE d.deptno=e.deptno(+);

SQL>SELECT e.empno, e.ename FROM dept d RIGHT OUTER JOIN emp e ON d.deptno=e.deptno;
SQL>SELECT e.empno, e.ename FROM dept d , emp e WHERE d.deptno(+)=e.deptno;

SQL>SELECT e.empno, e.ename FROM dept d FULL OUTER JOIN emp e ON d.deptno=e.deptno;

참고: full outer join의 비밀 [Science of DataBase]


PARTITION OUTER JOIN 

 - PARTITION OUTER JOIN을 사용하여 분석 대상이 되는 디멘션의 densification을 수행할 수 있으며, 
 - 파티션이나 테이블 내부에서 OUTER JOIN을 적용할 수 있습니다.

select hiredate,d.dname,  nvl(sum_sal,0) sum_sal
from dept d
left outer join (select deptno,to_char(hiredate,'YYYY') hiredate, sum(sal) sum_sal from emp group by deptno,to_char(hiredate,'YYYY')) e
partition by (e.hiredate)
on d.deptno = e.deptno
order by 1,2
/

HIREDATE     DNAME                                         SUM_SAL
------------ ------------------------------------------ ----------
1980         ACCOUNTING                                          0
1980         OPERATIONS                                          0
1980         RESEARCH                                          800
1980         SALES                                               0
1981         ACCOUNTING                                       7450
1981         OPERATIONS                                          0
1981         RESEARCH                                         5975
1981         SALES                                            9400
1982         ACCOUNTING                                       1300
1982         OPERATIONS                                          0
1982         RESEARCH                                         3000
1982         SALES                                               0
1983         ACCOUNTING                                          0
1983         OPERATIONS                                          0
1983         RESEARCH                                         1100
1983         SALES                                               0



NATURAL JOIN 

 - Equijoin과 동일하다고 보시면 됩니다.
 - 두 테이블의 동일한 이름을 가지는 칼럼은 모두 조인이 됩니다. (oracle forum : HR유저에서 NATURAL JOIN 결과가 다릅니다 )
 - 동일한 컬럼을 내부적으로 찾게 되므로 테이블 Alias를 주면 오류가 발생 합니다. 
    (ORA-25155: column used in NATURAL join cannot have qualifier)
 - 동일한 컬럼이 두개 이상일 경우 JOIN~USING문장으로 조인되는 컬럼을 제어 할 수 있습니다.
 
SQL>SELECT empno, ename, deptno FROM emp NATURAL JOIN dept 
SQL>SELECT e.empno, e.ename, d.deptno FROM emp e, dept d WHERE e.deptno=d.deptno


JOIN ~ USING 

 - NATURAL JOIN의 단점은 동일한 이름을 가지는 칼럼은 모두 조인이 되었는데 USING 문을 사용하면 컬럼을 선택해서 조인을 할 수가 있습니다.
 - USING절 안에 포함되는 컬럼에 Alias를 지정하면 오류가 발생 합니다.
  
SQL>SELECT e.empno, e.ename, deptno FROM emp e JOIN dept d USING(deptno)


ON 구문

 - 조인 조건을 지정 할 수 있습니다. 
 - 모든 논리 연산 및 서브쿼리를 지정할 수 있습니다.

SQL>SELECT e.empno, e.ename, e.sal
       FROM emp e JOIN dept    d  ON (e.deptno=d.deptno)
                           JOIN bonus  b ON (b.ename = e.ename)
       WHERE e.sal IS NOT NULL




* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!



옵티마이저는 실행 계획의 비용을 계산하기 위한 비용 모델(Cost Model)을 갖고 있고, 이 비용 모델은 Oracle Data Dictionary에서 관리하는 다양한 통계정보를 기반으로 크게 다음과 같은 세 가지 값(measure)의 예상치를 계산한다.

Q1:
select ename, sal
from emp e, dept d
where e.deptno = d.deptno and d.loc = ‘SEOUL’

선택도

우선 선택도(selectivity)의 개념을 예로 들자. 앞에서 예로 든 질의 Q1에서 d.loc = ‘SEOUL’이라는 조건의 선택도는 dept 테이블 전체 중에서 loc의 값이 ‘SEOUL’인 레코드의 비율을 일컫는다. 옵티마이저는 선택도 계산을 통해서 해당 조건을 만족하는 레코드가 몇 건 정도가 되는지를 예측하게 된다. 옵티마이저는 만일 DBA_TABLES에 dept 테이블의 loc 칼럼의 distinct column values가 10이라면 옵티마이저는 선택도가 0.1이라고 판단하게 된다. 이때 선택도를 이와 같이 정하는 이유는 dept 테이블이 loc 칼럼들에 골고루 분포되어 있다고 가정할 때 성립한다. 그러나, 실제로 loc 칼럼의 값들이 skew되어서 분포할 수도 있다.

예를 들어, 전체 레코드의 50%가 loc 값으로‘SEOUL’을 갖는다면 잘못된 선택도 값을 얻게 된다. 이와 같이 데이타 분포가 skew되어 있는 경우, 해당 칼럼에 대한 히스토그램 정보를 DBA_HISTOGRAM 테이블에 만들어 주어야 정확한 선택도 값을 계산할 수 있다(이 경우는 0.5). 오라클 옵티마이저는 다양한 조건식의 종류에 대해 선택도를 통계정보에 기반해서 계산하는 수식을 내부적으로 갖고 있다. 그렇지만, 만일 dept 테이블이 아직 분석되지 않아서 통계정보가 없는 경우, 옵티마이저는 내부적으로 갖고 있는 디폴트 값을 선택도로 지정한다(예를 들어, 0.01).

카디널러티

앞의 dept 테이블의 전체 레코드 건수가 1000일 때, 앞에서 설명한 loc = ‘SEOUL’의 선택도가 0.1로 계산되었을 때, 조건을 만족하는 레코드 건수는 1000 x 0.1, 즉 100개로 예상할 수 있다. 이와 같이 어떤 연산을 수행한 결과로 나오는 레코드 건수를‘카디널러티(cardinality)’라 하는데, 정확한 카디널러티를 계산하는  것은 좋은 실행 계획을 만드는 데 굉장히 중요하다.

예를 들어, (T1§_T2)§_T3 순서로 테이블을 조인할 때 (T1§_T2)의 결과와 T3를 조인할 때 어떤 조인 방법을 선택하는 것이 좋을지를 결정하기 위해서는 (T1§_T2)의 크기를 정확하게 알아야 한다. 이를 위해서는 (T1§_T2) 조인의 결과 레코드가 몇 개인지를 예상할 수 있어야 한다. 이를 위해 오라클 옵티마이저는 다양한 연산의 결과 레코드의 카디널러티를 통계정보와 수식에 의해서 계산한다. T1과 T2의 조인 조건이 T1.c1 = T2.c2(이를‘P’라
표기)라 했을 때, 앞에서 설명한 선택도 계산 공식에 의해 이 조건식의 선택도 Sel(P)를 먼저 계산한 후, 이 조인의 결과 카디널러티는 Card(T1) x Card(T2) x Sel(P)가 된다.

예를 들어, T1, T2의 튜플 수가 각각 1000, 5000이고 Sel(P)가 0.01이면, 조인의 결과로 생기는 튜플 수는 1000 x 5000 x 0.01 = 5000이 된다. 그런데, Sel(P)가 조금이라도 틀리면 이후의 전체적인 비용 산정이 잘못되게 된다. 오라클 옵티마이저는 다양한 종류의 연산에 대해 내부 공식을 사용해 카디널러티를 계산한다.

비용

비용(cost)은 테이블 액세스, 조인 등을 수행하는 데 걸리는 시간을 의미하는데, 시간은 주로 디스크 I/O 수와 CPU 사용시간을 고려한다. 비용은 앞에서 계산한 통계 정보와 내부 계산식에 의해 계산된다.

예를 들어, T1§_T2를 Nested Loop 방식으로 조인할 경우 조인비용은 (T1의 데이타 블록수) + ((T1의 레코드 건수)*(T2의 액세스 비용))이 된다. 이처럼 오라클 옵티마이저는 모든 연산에 대해 소요되는 비용을 계산하는 수식을 갖고 있다. 오라클 옵티마이저는 이 세 가지 예상 값(measure)을 기반으로, 현재의 실행 계획의 예상 비용을 구한다.

위 내용은 아래 첨부된 "Oracle Technical Note : 오라클 옵티마이저의 기본 원리"의 일부를 정리한 내용입니다.



* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!


 
오라클은 사용자 작업을 위해 많은 Package/Fuction 등 을 지원합니다. 
이런 놈들을 다 알필요는 없지만 아는 많큼 손발이 고생할 여지가 조금씩 줄어 듭니다. 그러니 기회가 있을 때마다 조금씩 알아 놓는게 좋겠죠 ^^;
얼마전 작업하다기 DBMS_SHARED_POOL.PURGE procedure를 유용하게 사용할 수 있는 기회가 있었어 간략하게 정리합니다. 
 
주말의 몇몇 table의 re-org 작업이 있었는데, 
시간 계산을 잘못해서 마지막 즈음에 기다리던 작업자를 위해 사용자 세션을 풀어 주었습니다. 주말이라 얼마나 들어오겠나 싶었죠.. 
많은 세션들이 들어오진 않았지만, 몇몇 세션들이 주로 수행되는 SQL을 이것 저것 수행하고 나갔습니다. 
 
근데 작업 전보다 성능이 않좋다고 하더군요. 
지금 re-org 작업이 있으니 조금 느릴 수도 있겠다 싶었죠. 근데, 작업 후에도 느리다고 하더군요. 
Plan이 바뀌었습니다. 통계정보도 같이 넣어 주었는데 말이죠.. 
 
import로 data를 loading 하면 마지막에 해당 segment의 통계정보를 같이 적제하게 됩니다. 근데 통계정보가 다 들어가기 전에 수행된 SQL은 통계정보가 없는 상태에서 cost를 계산해서 sql plan을 만들어 버리죠. 또 이렇게 만들어진 plan 정보는 해당 cursor가 purge 되기 전까지는 계속 사용하게 됩니다. 
 
이러한 비슷한 경우는 bind peeking 기능에 의해 일반적이지 않은 bind 값이 들어가도 발생할 수가 있습니다. 
 
이러한 경우 shared pool flush를 시켜 해당 cursor를 purge 시킬 수 도 있겠지만 11g 이후 부터는 단일 cursor 별로 purge가 가능합니다. 10.2.0.4 version에서도 사용가능한데, 이 버전의 경우 event 설정이 필요합니다. 
 
event="5614566 trace name context forever"
다음의 내용은 참조 노트의 사용 방법에 대한 간단한 예 입니다. 
 
SESSION 1 
 
sqlplus scott/tiger 
SQL> select ename from emp where empno=7900;
 
SESSION 2 
 
sqlplus / as sysdba 
 
SQL> select address, hash_value, executions, loads, version_count, invalidations, parse_calls 
     from v$sqlarea 
     where sql_text = 'select ename from emp where empno=7900';
 
ADDRESS          HASH_VALUE EXECUTIONS      LOADS VERSION_COUNT INVALIDATIONS PARSE_CALLS 
---------------- ---------- ---------- ---------- ------------- ------------- ----------- 
000000007A6CF430 1052545619          1          1             1             0           1
 
SQL> exec dbms_shared_pool.purge ('000000007A6CF430,1052545619 ','C'); 
 
SQL> select address, hash_value, executions, loads, version_count, invalidations, parse_calls 
     from v$sqlarea 
     where sql_text = 'select ename from emp where empno=7900';
 
no rows selected
 
 
이 내용은 아래의 문서를 참조했습니다. 
 
Note 457309.1 How To Flush an Object out the Library Cache 
Note 751876.1 DBMS_SHARED_POOL.PURGE Is Not Working On 10.2.0.4
 

Oracle 9i Release 2 Enterprise Edition 에서 소개된 TABLE Compress 기능은 블록 내에 저장되어 있는 데이터의 중복을 제거 함으로써 블록 내에 많은 데이터를 저장함으로써 저장공간의 효율성을 높이는 기술입니다.

반복되는 데이터를 블록의 시작 부분에  symbol table의 row로 생성하고 실재 row data는 이 row의 참조로 대체합니다. 해당 block의 row에 대한 데이터는 global symbol table이 아닌 해당 block내의 local symbol table로 만들어집니다.

따라서 블록에서 압축되지 않은 데이타를 다시 만드는데 필요한 모든 정보를 해당 블록 내에서 사용할 수 있습니다.

블록의 시작 부분에 있는 심볼 테이블을 제외하고 압축된 데이타베이스 블록은 일반 데이타베이스 블록과 비슷합니다.  결과적으로 일반 데이타베이스 블록에서 작동하는 모든 데이타베이스 기능은 압축된 데이타베이스 블록에서도 동일하게 작동한다고 합니다.

Table compress 기능은 storage의 높은 활용도와 질의시 적은 I/O양, buffer cache내의 압축된 형태 유지로 더 많은 데이터를 메모리 내에 유지하는 등의 장점을 갖고 있으나, DML 작업은 compress가 적용되지 않은 일반 table에 비해 느리며, CPU 사용률이 높아지는 단점도 가지고 있습니다.

compress의 경우 다른 고려 사항없이 compress 만 지정하면 oracle에서 자동으로 compress를 진행합니다. 대부분의 자료에서는 2:1 ~ 4:1 정도의 compress 비율을 말하지만 data의 속성, 군집 여부 등 많은 변수에 의해 편차가 많아 실 데이터의 sampling으로 compress 비율을 확인하는 것이 그나마 가장 확실 합니다.

compress는 동일 column data가 2개 이상이면 compress 대상이 되며, 서로 다른 column에 같은 값이 존재해도 이에 대해 compress를 하게 됩니다.

compress가 되는 시점은 한개의 block내에 pctfree에 도달하면 compress를 하며 compress 후 빈 공간에 non-compress row를 insert 하고 pctfree에 도달하면 다시 compress.. 를 반복하게 됩니다.

2010/05/04 Update:

UPDATE, INSERT가 많은 table에 대해서 COMPRESS optoin 사용은 자제 해야 되겠네요. 
이틀동안 UPDATE 문 성능으로 헤매다가 결국 OLTP COMPERSS 문제인 걸 확인했네요. --;

In general table compression is good for read only operations.Following is excerpt from "Table Compression" section of Data Warehousing Guide manual, which suggests that it is not a good idea to compress a table which expects a large update,because there are CPU overheads and big space consumption :
Table compression should be used with highly redundant data, such as tables with many foreign keys. You should avoid compressing tables with much update or other DML activity. Although compressed tables or partitions are updatable, there is some overhead in updating these tables, and high update activity may work against compression by causing some space to be wasted.






Faster Startup


The days when dinosaurs ruled the earth and 2GB memory was considered large are gone. Now, it's not uncommon to see large buffer caches to the tune of 100GB. When the instance is started, it might take several minutes, or even hours, to initialize a buffer cache of this size.

If you look deeper into the situation, you will notice that the entire buffer cache need not be up when the database instance starts. After the instance starts, the buffer cache is empty, which gradually fills up when users select data from tables. So, there is no need to initialize the entire buffer cache when the instance starts.

In Oracle Database 10g Release 2, this behavior is accounted for in the startup logic. When you start the instance, only 10% of the buffer cache is initialized; the rest is initialized after the database is opened by the checkpoint process. This new approach reduces instance startup time significantly.

Bear in mind, however, that until the entire buffer cache is initialized, automatic buffer cache sizing is not available.

아시다 시피 SGA memory 영역은 Database가 nomount 단계에서 할당됩니다. 
이 new feature는 nomount 단계에서는 10%의 buffer pool만 할당하고 나머지는 instance가 open 된 후에 할당한다는 의미입니다.


Drop Empty Datafiles


Imagine that you just added a datafile to the wrong directory or tablespace—a fairly common error. All is not lost; the datafile doesn't contain any data yet, so you can easily drop it, right?

Unfortunately, you can't. Prior to Oracle Database 10g Release 2, your only clean option for removing a datafile is to drop the entire tablespace and then rebuild it without that particular file. If the tablespace contains data, you have to go through the time-consuming and laborious process of storing the data on a separate location and reinstating it. In addition to its inconvenience, this process makes the tablespace unavailable.

Thankfully, in Oracle Database 10g Release 2 the process has been simplified: You can just drop the datafile. For example, the following command will remove the indicated datafile from the tablespace as well as from the server.
alter tablespace users drop datafile '/tmp/users01.dbf'
 /
There are a couple restrictions, however: The datafile must be empty to be dropped. You can't drop the last datafile in a tablespace; the tablespace itself must be dropped. And the tablespace must be online and in read-write status.

10g 이전 버전에서는 데이터파일을 잘 못 붙일 경우 이거 띄어 내려면 tablespace 전체를 재구성해야 하죠. 
예전 OPS 쓸때 raw device가 아닌 filesystem에 datafile을 추가하는 등의 황당한 경우도 있었습니다.. ㅋ
drop empty datafiles new feature는 이런 유저 실수로 인한 막대한 작업을 더이상 안해도 되게 해주겠군요. 


Catch the Error and Move On: Error Logging Clause


Suppose you are trying to insert the records of the table ACCOUNTS_NY to the table ACCOUNTS. The table ACCOUNTS has a primary key on ACC_NO column. It's possible that some rows in ACCOUNTS_NY may violate that primary key. Try using a conventional insert statement:
SQL> insert into accounts
  2  select * from accounts_ny;
insert into accounts
*
ERROR at line 1:
ORA-00001: unique constraint (ARUP.PK_ACCOUNTS) violated
None of the records from the table ACCOUNTS_NY has been loaded. Now, try the same with error logging turned on. First, you need to create a table to hold the records rejected by the DML statement. Call that table ERR_ACCOUNTS.
exec dbms_errlog.CREATE_ERROR_LOG ('ACCOUNTS','ERR_ACCOUNTS')
Next, execute the earlier statement with the error-logging clause.
SQL> insert into accounts
  2  select * from accounts_ny
  3  log errors into err_accounts
  4  reject limit 200
  5  /

6 rows created.
Note that the table ACCOUNTS_NY contains 10 rows yet only six rows were inserted; the other four rows were rejected due to some error. To find out what it was, query the ERR_ACCOUNTS table.
SQL> select ORA_ERR_NUMBER$, ORA_ERR_MESG$, ACC_NO
 2  from err_accounts;

ORA_ERR_NUMBER$   ORA_ERR_MESG$                                       ACC_NO
 ---------------  --------------------------------------------------  ------
               1  ORA-00001: unique constraint (ARUP.PK_ACCOUNTS) vi  9997
               olated
               1  ORA-00001: unique constraint (ARUP.PK_ACCOUNTS)vi  9998
               olated
               1  ORA-00001: unique constraint (ARUP.PK_ACCOUNTS) vi 9999
               olated
               1  ORA-00001: unique constraint (ARUP.PK_ACCOUNTS) vi 10000
               olated
Note the columns ORA_ERR_NUMBER$, which show the Oracle error number encountered during the DML statement execution, and the ORA_ERR_MESG$, which shows the error message. In this case you can see that four records were rejected because they violated the primary key constraint PK_ACCOUNTS. The table also captures all the column of table ACCOUNTS, including the column ACC_NO. Looking at the rejected records, note that these account numbers already exist in the table; hence the records were rejected with ORA-00001 error. Without the error-logging clause, the whole statement would have failed, with no records rejected. Through this clause, only the invalid records were rejected; all others were successful.

이 기능은 sql*loader의 bad file 같은 기능입니다. 
특정 에러가 발생해도 이를 logging만 하고 나머지 데이터는 처리를 해주는 군요.  
PK constraint violation 발생하면 이거 처리하는 것도 좀 귀찮은 작업인데, 요 기능 쓰면 약간 편해지겠습니다. ^^

그나 저나 아직도 10g new feature를 모르고 있다니... 좌절이다~



* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!



Oracle Database Migration이나 데이터 정리 등을 하려 TRUNCATE 명령 등을 수행할 때 ORA-2266에러를 만날 수 있습니다. 
요 에러는 reference constraint key가 걸려 있어 명령을 수행할 수 없다는 에러인데, 내가 작업하려 하는 table의 PK를 누가 참조하고 있다는 의미죠. 

> oerr ora 2266
02266, 00000, "unique/primary keys in table referenced by enabled foreign keys"
// *Cause: An attempt was made to truncate a table with unique or 
//         primary keys referenced by foreign keys enabled in another table.
//         Other operations not allowed are dropping/truncating a partition of a
//         partitioned table or an ALTER TABLE EXCHANGE PARTITION.
// *Action: Before performing the above operations the table, disable the 
//          foreign key constraints in other tables. You can see what 
//          constraints are referencing a table by issuing the following 
//          command:
//          SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = "tabnam";

이런 경우 dba_constraints를 query해서 어떤 table에서 reference 하고 있는지 확인할 수 있습니다. 
간단한 SQL이지만 계속 만들기 귀찮아 한번 정리해 봅니다. 

column r_owner format a20
column r_constraint_name format a30
column owner format a20
column table_name format a30
column constraint_name format a30
column "Constraint Disable COMMAND" format a150
set linesize 200
set pagesize 200
select r_owner,r_constraint_name,owner,table_name,constraint_name,constraint_type
from dba_constraints
where r_constraint_name in (select constraint_name from dba_constraints where table_name = upper('&&TABLE_NAME'))
/
select 'alter table '||owner||'.'||table_name||' disable constraint '||constraint_name||';' "Constraint Disable COMMAND"
from dba_constraints
where r_constraint_name in (select constraint_name from dba_constraints where table_name = upper('&TABLE_NAME'))
/

위의 SQL을 돌리면 아래 처럼 reference 하고 있는 table과 해당 constraint가 나옵니다. 
또 요놈들을 disable 해 줄 수 있는 명령도 같이 출력하게 됩니다. 

R_OWNER              R_CONSTRAINT_NAME              OWNER                TABLE_NAME                     CONSTRAINT_NAME                CON
-------------------- ------------------------------ -------------------- ------------------------------ ------------------------------ ---
DBSNMP               BSLN_BASELINES_PK2             DBSNMP               BSLN_STATISTICS                BSLN_STATISTICS_FK             R
DBSNMP               BSLN_BASELINES_PK2             DBSNMP               BSLN_THRESHOLD_PARAMS          BSLN_THRESHOLDS_FK             R

Constraint Disable COMMAND
------------------------------------------------------------------------------------------------------------------------------------------------------
alter table DBSNMP.BSLN_STATISTICS disable constraint BSLN_STATISTICS_FK;
alter table DBSNMP.BSLN_THRESHOLD_PARAMS disable constraint BSLN_THRESHOLDS_FK;

물론 아래처럼 cascade로 다 disable 시킬 수 있지만, 눈으로 한번 확인하고 작업하는 이 신중함 땜에.. ㅋ

ALTER TABLE dept DISABLE PRIMARY KEY CASCADE;


* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!



DECODE() Function는 return된 결과가 지정된 값과 동일할 경우 특정 value를 출력해주는 function이다.  DECODE()는 다음과 같은 형태로 사용된다.

DECODE(value, search_value, result, default_value)

위의 decode function은 미리 지정된 value와 search_value가 같을 경우 result를, 그렇지 않은 경우 default_value를 출력한다. 이와 같이 decode()는 SQL에서 PL/SQL없이 IF-THEN-ELSE logic을 구현하고 있다.

다음은 decode()의 간단한 동작 예이다.

SELECT DECODE(1, 1, 2, 3)
FROM dual;

DECODE(1,1,2,3)
---------------
              2


SELECT DECODE(1, 2, 1, 3)
FROM dual;

DECODE(1,2,1,3)
---------------
              3

다음의 예는 more_products table의 available column의 값을 비교해 원하는 string을 출력한다. available column의 값이 'Y'일 경우 "Product is available'을 출력하고, 'Y'가 아닐 경우 'Product is not available"을 출력한다.

SELECT prd_id, available,
   DECODE(available, 'Y', 'Product is available',
      'Product is not available')
FROM more_products;

    PRD_ID A DECODE(AVAILABLE,'Y','PR
---------- - ------------------------
         1 Y Product is available
         2 Y Product is available
         3 N Product is not available
         4 N Product is not available
         5 Y Product is available

다음은 product table의 product_type_id column의 값들에 따라 원하는 string을 출력한다. 즉 product_type_id가 1일 경우 'Book', 2일 경우 'Video', 3일 경우 'DVD', 4일 경우 'CD', 기타 다른 값일 경우 'Magazine'을 출력한다.

SELECT product_id, product_type_id,
 DECODE(product_type_id,
   1, 'Book',
   2, 'Video',
   3, 'DVD',
   4, 'CD',
   'Magazine')
FROM products;

PRODUCT_ID PRODUCT_TYPE_ID DECODE(P
---------- --------------- --------
         1               1 Book
         2               1 Book
         3               2 Video
         4               2 Video
         5               2 Video
         6               2 Video
         7               3 DVD
         8               3 DVD
         9               4 CD
        10               4 CD
        11               4 CD
        12                 Magazine

Decode()은 CASE문에 비해 depth가 깊어지면 성능이 떨어지는 것으로 알려져 있다. depth가 깊은 SQL의 경우 CASE()와의 성능 비교가 필요할 수 있다.


* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!



서점에서 몇번 오라클 관련 서적을 샀었는데, 많이 실망한 적이 많이 있었습니다.
그때는 오라클에 대해 잘 모를 때였던 거 같은데,
나중에 보니 tablespace를 작업공간이라고 해석해 놓았던 책도 있더군요.. --;

다음의 리스트는 오라클 전문가가 되기 위해 읽을만한 추천책입니다.

물론 이제 막 십년 밖에 안된 야매인 제가 추천해드리는 책은 아니고,
http://oracledoug.com 블로그를 운영하는 "Douglas Ian Burns"라는 사람이 추천한 책입니다.
이분은 자그마치 18년의 경력을 갖고 있다시는 군요..
저도 18년 정도 하면 이분 정도의 공력을 갖을 수 있을까..

걱정입니다.. --;
1. Chris Date's Database In Depth from O'Reilly
2. Oracle Concepts manual
3. Expert One-on One: Oracle Written by Tom Kyte
4. Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions
5. Practical Oracle 8i
6. Cost Based Oracle: Fundamentals
7. Optimizing Oracle Performance
8. Oracle Wait Interface: A Practical Guide to Performance Diagnostics and Tuning
 

원문: http://oracledoug.com/books.html

이분이 한국인이 아니기 때문에 당연히 외국 원서 입니다. ㅋ
국내에 번역 된 책도 많이 있는데, 알라딘에서 찾아 링크 좀 걸까 했는데, 국내에서는 절판이나 품절이 많네요.




일반적인 HOT block에 따른 현상이 널리 알려진게 'cache buffer chains' 등의 buffer chain에 대한 latch 입니다. 

다음의 SQL은 cache buffers chains children latch 중 가장 sleep count가 높은 children latch의 ADDR에 어떤 block들이 달려 있는지 확인해
그 block 중 가장 동시 접근이 많은 block을 찾는 SQL 입니다. 

select * from (
select HLADDR,TS#,FILE#,DBABLK,TCH , CHILD#,GETS,MISSES,SLEEPS
from x$bh x, 
    (select * from (
     select CHILD#  ,ADDR,GETS,MISSES,SLEEPS  from v$latch_children where name = 'cache buffers chains' order by 5 desc
                   ) 
     where rownum < 2 ) y
where x.hladdr = y.addr
order by tch desc
) where rownum < 2
/





Oracle global temporary table은 Transaction 내에서 임시로 만들 수 있는 table을 지칭합니다. 
transaction 내부에서 사용하는 것이니 당연히 transaction을 commit/ rollback하면 없어지겠죠. 

"global"이라는 단어가 붙은 이유는 table의 구조는 공유한데서 나오니 않았나 싶습니다. 
같은 global temporary table에 각각의 세션에서 데이터를 insert 하면 서로 다른 값을 보여주게 됩니다. 

Global temporary table 생성 명령은 table 생성명령에 global temporary 만 넣어주면 됩니다. 

create global temporary table test_temp ..

global temporary table을 만든 후 dba_objects에서 query를 해보면 해당 이름으로 object_type이 table이라고 나오긴하나, 
dba_segments에서는 해당 이름의 segment는 없는 것으로 보여줍니다. v$sort_usage를 보니 temp tablespace에 temporary data segment type으로 한덩어리 만들어 놓았군요..






오라클의 전체적인 사용현황과 그에 따른 AWR, ADDM을 수행하기 좋은 툴하나 소개합니다. 
OraScope.Net이라는 툴인데, FreeWare 입니다. 

Session, Lock, Database 등 모니터링 기능은 없는 듯하나, 
Database 전체를 파악하기 위한 resource 사용현황들은 거의 다 있는 것 같습니다. 

Download는 아래사이트에 방문하시면 됩니다..

OraScope.Net LITE 는 FREEWARE 입니다.

 

즉, 무료 오라클 성능 모니터링 툴입니다.

개인, 기업, 기관등 사용자와 장소에 구애받지 않고, 마음대로 사용하실수 있습니다.

다만, OraScope.Net LITE 를 저작자의 동의없이 상업적 용도로는 사용할수 없으며,

본 싸이트에서 제공하는 배포화일 형태 그대로, 자유로이 배포하실수 있습니다.(배포화일의 재 PACKAGING은 불허)

 

OraScope.Net LITE 의 모든 저작권은 저작자( orascope@orascope.net ) 에게 있습니다.

 

OraScope.Net LITE 를 사용하여 일어나는 문제나 기타 제반 사항에 대해, 저작자는 책임이 없음을 알려드립니다.

(기타 후원해 주실 분이나 업체는 orascope@orascope.net 로 연락주시면 됩니다.Laughing)









뻔한 script 지만, 나름 쓸만한 shell script 입니다.
shell로 저장한 후 뒤에 sql script와 interval을 주만 무한 루프..
active session 모니터링용으로는 뭐 GUI tool이 부럽지 않죠.. ㅋ

<사용방법>
rpt <sql script name> <interval(sec)>

<내용>
if [ $# -eq 1 ]
then
 arg2=2
elif [ $# -eq 2 ]
then
 arg2=$2
else
     echo "Usage : rpt sql term";
     exit;
fi

arg1=$1
while true
do
sqlplus -s '/ as sysdba' <<EOF
set linesize 200
set pause off
set pagesize 120
@$arg1
EOF
sleep $arg2
done




Oracle External table은 오라클 외부의 데이터를 내부로 loading해 table 형식으로 보여 줍니다.
Oracle 11g R2에서는 "PREPROCESSOR 절"이 생겨 이 외부 file에 대해 선처리 프로세서를 정의해 줄 수 있습니다.

대부분 외부 SAM file 형식이니 압축해제나 shell을 통한 추출 정도의 선 프로세서를 정의할 수 있겠네요.
잘만 된다면 SAM file 저장하는 공간을 줄일 수 있겠네요.

예 : compress된 데이터를 gunzip을 이용해 압축 해제 후 loading

CREATE TABLE sales_transactions_ext
     (PROD_ID NUMBER, CUST_ID NUMBER, TIME_ID DATE, CHANNEL_ID NUMBER, PROMO_ID NUMBER, QUANTITY_SOLD NUMBER,
      AMOUNT_SOLD NUMBER(10,2), UNIT_COST NUMBER(10,2), UNIT_PRICE NUMBER(10,2))
ORGANIZATION external
 (TYPE oracle_loader DEFAULT DIRECTORY data_file_dir
  ACCESS PARAMETERS
  (RECORDS DELIMITED BY NEWLINE CHARACTERSET US7ASCII
   PREPROCESSOR exec_file_dir:'gunzip' OPTIONS '-c'
   BADFILE log_file_dir:'sh_sales.bad_xt'
   LOGFILE log_file_dir:'sh_sales.log_xt'
   FIELDS TERMINATED BY "|"
   LDRTRIM ( PROD_ID, CUST_ID, TIME_ID DATE(10) "YYYY-MM-DD", CHANNEL_ID, PROMO_ID, QUANTITY_SOLD,
                AMOUNT_SOLD, UNIT_COST, UNIT_PRICE))
location ('sh_sales.dat.gz') )
REJECT LIMIT UNLIMITED;






오라클의 통계정보는 다른 DB나 계정으로 옮겨질 수 있습니다.
개발장비에서 수집된 통계정보를 옮긴다거나, 기존의 운영장비의 통계정보를 migration 할 대상으로 옮겨 동일한 SQL 수행을 유도할 수 있습니다.

다음은 현재 SCOTT 계정의 통계정보를 다른 Database의 SCOTT 계정으로 옮기는 예제입니다.

* statistic 정보 수집 table 생성
SQL> exec dbms_stats.create_stat_table('SCOTT','STATS');

* scott.emp table의 통계정보를 statistic 정보수집 테이블인 STATS로 export
exec dbms_stats.export_table_stats('SCOTT','EMP',NULL,'STATS',NULL,TRUE);

* STATS table export
%exp scott/tiger tables=STATS file=expstat.dmp

* STATS table import
%imp scott/tiger file=expstat.dmp full=y log=implog.txt 

* STATS table의 통계정보를 SCOTT.EMP에 import
exec dbms_stats.import_table_stats('SCOTT','EMP',NULL,'STATS',NULL,TRUE);

기존의 통계정보를 다른 Database의 동일 계정으로 옮기는 작업에 대한 내용들은 많이 기술 되어 있는데,
이름이 다른 계정은 어떻게 옮길까요?

다른 계정으로 통계정보를 옮기는 작업은 다른 데이터베이스내의 동일 계정으로 통계정보를 옮기는 작업처럼 procedure로만은 불가능합니다.
물론 작업 자체가 불가능하다는 이야기는 아닙니다.

You may not export stats from one schema name and import into a different schema name (Bug 1077535). The schema names much match exactly. If the target database schema name (import database) is different from the source database schema name (export database), then you may update the table you exported the statistics into and set the C5 column to the target schema name.

통계정보를 export 받기 전에 생성하는 statistic 정보를 저장하는 stat table에는 통계정보가 만들어진 계정의 이름이 지정되어 있어,
이를 다른 계정으로 import하려고 하면 import는 성공했다고 나오나 통계정보는 입력 되지 않습니다.

따라서 수집된 통계정보가 있는 table의 owner column의 owner를 바꿔줘야 합니다.

i.e.
"update table sd_stat set c5 = '<target schemaname>'
where c5 = '<Source Schema name>'
  and statid = '<Stat Id used while exporting these stats>;"

update sd_stat set c5 = 'JBARLOW';
where c5 = 'SCOTT'
  and statid = 'a';
commit;

정리해 보자면 SCOTT 계정의 SD table의 통계정보를 JBAARLOW 계정으로 옮긴다면 아래와 같이 수행해야 합니다.

* statistic 정보 수집 table 생성
SQL> exec dbms_stats.create_stat_table('SCOTT','SD_STAT');

* scott.sd table의 통계정보를 statistic 정보수집 테이블인 SD_STATS로 export
exec dbms_stats.export_table_stats('SCOTT','SD',NULL,'SD_STAT',NULL,TRUE);

*  SD_STAT table의 계정정보 수정
update sd_stat set c5 = 'JBARLOW';
where c5 = 'SCOTT';
commit;

* STATS table의 통계정보를 JBARLOW.SD table에 import
exec dbms_stats.import_table_stats('JBARLOW','SD',NULL,'SD_STAT',NULL,TRUE,'SCOTT');


참고 : Note 117203.1 How to Use DBMS_STATS to Move Statistics to a Different Database





Oracle INVISIBLE index는 Oracle 11g new feature 입니다.

말 그대로 보이지 않는 index 입니다. 여기서 보는 주체는 oracle optimizer가 됩니다.
즉 index는 존재하지만 optimizer는 이를 기준으로 plan을 생성하지 않습니다.
그러나 해당 index의 table에 대한 DML 변경 내역은 모두 index에 적용되게 됩니다.

이는 index 생성에 따른 혹은 index 삭제에 따른 전체 성능, 일부 성능 측정에 도움이 될 만한 feature가 아닌가 싶습니다.

Invisible index 생성
SQL> Create index invisible_index on table(column) invisible;

Invitible index 사용 정의
SQL> alter session set optimizer_use_invisible_indexes=true;

Invisible index를 visible로 변경
SQL> alter index invisible_index visible;

visible index를 invisible로 변경
SQL> alter index invisible_index invisible;





Oracle과 다른 DB와의 차이점을 보면 굉장히 손이 많이 갑니다.
oracle 10g, 11g가 나오면서 운영의 자동화와 간소화 기능이 많이 추가 되긴 했지만요..

그런데, 이말을 거꾸로 말해보자면, 사용자의 역량에 따라 tuning 할 수 있는 포인트가 많다는 이야기도 되죠.
또 이런 특성때문에 곳곳에 숨어 있는 고수들도 많이 있습니다.

아래의 SQL은 숨어 있는 이러한 기능들을 control 할 수 있는 parameter를 확인 할 수 있는 SQL 입니다.
물론 show parameter나 v$parameter에서 parameter의 값을 확인 할 수 있지만,
이 두개의 방법으로 찾을 수 있는 parameter는 일반 parameter와 변경된 hidden parameter만을 볼 수 있습니다.

아래의 SQL은 일반 parameter와 hidden parameter 모두 확인 가능한 parameter 입니다.

col value format a20
col description format a70
select a.inst_id "inst_id",  ksppinm "name",  ksppstvl "value",  ksppdesc "description"
from x$ksppi a,x$ksppsv b where a.indx = b.indx and ksppinm like '%&parameter%'
/

SQL만 덩그러니 post하긴 좀 그러니..
각 버전 별로 전체 parameter 갯수와 hidden parameter 개수를 비교해 봤습니다.

10g R2는 전체 1385개의 parameter 중 hidden parameter는 1127 개이고,
11g R1 은 1920개 중 1631개,
11g R2는 2399개 중 2057 개  입니다.
(제 VMware linux 서버 기준입니당)

버전이 높아 질수록 점점 숨겨지는 parameter도 늘어가네요. 
또 전체 parameter 중에서 감춰진 parameter가 대부분이군요..
약 70% 정도 되는 거 같네요.. 

요거 마스터 하면 고수가 될 수 있을 라나..




AWR을 보다 보니 낯선 enqueue 2개가 자주 보여 요거 좀 정리합니다.

RO-Multiple Object Reuse (fast object reuse)

RO enqueue는 "multi object reuse" enqueue로 알려져 있습니다. 이 Enqueue는 foreground process와 background process 간의 sync 하는데 사용되는 enqueue 입니다. Background는 주로 DBWR나 CKPT를 말합니다.

특별히 object drop이나 table truncate 할 때 많이 사용됩니다.

oracle database내에서 truncate나 drop이 발생하면 다음과 같은 내부 작업들이 수행됩니다.

1. foreground process는 먼저 "RO" enqueue를 "execlusive" mode로 요청합니다.
2. 다음은 instance에 작업을 요청하는 cross instance call이 발생되어, CI enqueue가 할당됩니다.
3. 각 instance의 CKPT는 CI call의 요청에 따라 DBWR에서 dirty buffer를 write하게하고 관련 buffer를
   invalidate 합니다.
4. DBWR가 write 작업을 끝내면 foreground process는 "RO" enqueue를 release 합니다.

사실상 이 enqueue 작업은 truncate/drop operation을 순차적으로 수행되기 때문에 자주 drop/truncate가 발생하면 "RO" enqueue contention이 발생할 수 있겠죠..

KO-Multiple Object Checkpoint (fast object checkpoint)

얼마전에 PDML 관련해 posting 한 내용 중 다음과 같은 글을 쓴적 있습니다.

"direct-path read가 발생하면 변경되었거나 disk에 반영되지 않은 데이터를 buffer cache에서 disk로 강제로 flush 한다. 그후 data를 direct path I/O로 읽는다."
Parallel Query Execution

oracle 10g R2이전엔 direct-path read가 발생하면 관련 segment가 저장된 tablespace를 대상으로 flush를 하였습니다. 그러나 oracle 10g R2이후엔 관련 object만 disk로 flush하게 바뀌었습니다.
tablespace 단위로 flush 할 경우 해당 tablespace에 많은 데이터가 저장되어 있거나 한 tablespace에 데이터를 몰아 넣었다면 엄청 불필요한 dirty buffer write가 발생할 수 있겠죠.

KO enqueue는 10g R2 부터 바뀐 이러한 동작에 발생하는 enqueue lock type 입니다. 즉 object에 대한 checkpoint 시 대기할 때 발생하는 enqueue lock입니다.


다음은 일반적인 Enqueue 성능을 확인하는 script 들 입니다.
Enqueue는 v$system_event  v$session_wait  v$enqueue_stat을 통해 각종 statistic 정보를 확인할 수 있습니다.

전체 database system 내의 wait event에 대한 wait 관련 정보

select EVENT,TOTAL_WAITS,TOTAL_TIMEOUTS,TIME_WAITED,AVERAGE_WAIT,WAIT_CLASS
from v$system_event
where wait_class not in ('Idle')
order by 4 desc
/

전체 enqueue lock에 대한 사용 정보
select * from v$enqueue_stat order by 7 desc
/


이 포스트는 다음의 문서를 참고했습니다.

http://ww.orafaq.com/usenet/comp.databases.oracle.server/2006/09/23/1586.htm
Note 286363.1 Truncate Takes A Long Time -- Waits on RO enqueue





Table partitioning은 Oracle의 대표적인 DW를 위한 기능이다.

이 기능은 테이블을 특정 partition set으로 나누어 관리 및 성능 상의 이점을 얻는다.
다음의 내용은 이러한 partition table의 종류와 간단한 정의이다.

Range Partitioning Method

column 값의 범위에 따라 partition을 나누는 가장 일반적인 방법이다. 이 방식은 대개 날짜와 같은 범위로 많이 나뉘게 되는데, AP의 성격에 따라 데이터가 특정 partition에 몰리는 경우 hash partition 방식이나, list partition 방식 등 다른 partition 방식을 통해 sub-partition을 나누기도 한다.


Create table test164874 (
ord_day         NUMBER(2),
ord_month       NUMBER(2),
ord_year        NUMBER(4),
ord_id  NUMBER(10)
)
storage (initial 12k next 12k pctincrease 0 minextents 1)
PARTITION BY RANGE (ord_year,ord_month,ord_day)
(
PARTITION P1 VALUES LESS THAN (2001,3,31) TABLESPACE PART1,
PARTITION P2 VALUES LESS THAN (2001,6,30) TABLESPACE part2,
PARTITION P3 VALUES LESS THAN (2001,9,30) TABLESPACE part3,
PARTITION P4 VALUES LESS THAN (2001,12,32) TABLESPACE part4
)
/


Hash Partitioning Method

partition key의 hash 값에 의해 partition으로 나눠진다. 일반적으로 균등한 분포를 가지며 성능상의 이유로 hash partition을 사용하기도 한다.


CREATE TABLE tabpart1(
ord_id   NUMBER(5),
ord_date DATE
)
PARTITION BY HASH(ord_id) PARTITIONS 16
STORE IN (tbs1,tbs2,tbs3,tbs4)
/


Composite Partitioning Method

Oracle 10g 이전엔 Range-List, Range-Hash composite partition 만 지원했으나, Oracle 11에서는 List-List, List-Hash, List-Range and Range-Range composite partition이 지원된다. 또 interval partition 방식에 대해서 Interval-Range, Interval-List and Interval-Hash가 가능하다.


   

CREATE TABLE TAB1(
     ord_id  NUMBER(10),
     ord_day    NUMBER(2),
     ord_month  NUMBER(2),
     ord_year   NUMBER(4)
     )
PARTITION BY RANGE (ord_year,ord_month,ord_day)
  SUBPARTITION BY HASH(ord_id) SUBPARTITIONS 8
  STORE IN (TBS1,TBS2,TBS3,TBS4,TBS5,TBS6,TBS7,TBS8)
     (  PARTITION P1 VALUES LESS THAN (2001,3,31),
        PARTITION P2 VALUES LESS THAN (2001,6,30),
        PARTITION P3 VALUES LESS THAN (2001,9,30),
        PARTITION P4 VALUES LESS THAN (2001,12,31)
 )
/


List Partitioning Method

Oracle 9i에 소개된 partition 방식으로 partition key의 값 자체에 의해 분리된 partition으로 데이터가 저장된다. 값에 의해 데이터가 partition 되므로 전혀 관계없는 데이터 값을 하나의 partition에 저장할 수 있다.

CREATE TABLE sales_list
(salesman_id NUMBER(5),
salesman_name VARCHAR2(30),
sales_state VARCHAR2(20),
sales_amount NUMBER(10),
sales_date DATE)
PARTITION BY LIST(sales_state)
(
PARTITION sales_west VALUES('California', 'Hawaii'),
PARTITION sales_east VALUES ('New York', 'Virginia', 'Florida'),
PARTITION sales_central VALUES('Texas', 'Illinois')
PARTITION sales_other VALUES(DEFAULT)
);

Interval Partitioning Method

Interval partition은 range partition 기능의 확장으로 개별 범위를 명시적으로 정의하지 않아도 해당 interval에 속하는 데이터가 입력 될 때 partition이 자동으로 생성된다.

CREATE TABLE interval_sales
( prod_id NUMBER(6)
, cust_id NUMBER
, time_id DATE
, channel_id CHAR(1)
, promo_id NUMBER(6)
, quantity_sold NUMBER(3)
, amount_sold NUMBER(10,2) )
PARTITION BY RANGE (time_id)
INTERVAL(numtodsinterval(7,'day'))
( PARTITION p0 VALUES LESS THAN (TO_DATE('1-1-2007', 'DD-MM-YYYY')) );

Reference Partitioning Method

Oracle 11g에서 소개된 partition 방식으로 “모-자” 관계의 table 간에 모 table의 partition key column의 복제 없이 모 table의 partition 구조를 따라가게 됩니다.

SQL> CREATE TABLE ref_part_parent
2 (pcol1 NUMBER PRIMARY KEY,
3 pcol2 VARCHAR2(10))
4 PARTITION BY RANGE (pcol1)
5 (PARTITION p1 VALUES LESS THAN (100),
6 PARTITION p2 VALUES LESS THAN (200),
7 PARTITION p3 VALUES LESS THAN (300),
8* PARTITION p4 VALUES LESS THAN (MAXVALUE))

Table created.

SQL> CREATE TABLE ref_part_child
2 (ccol1 NUMBER NOT NULL,
3 CONSTRAINT ccol1_fk FOREIGN KEY(ccol1) REFERENCES ref_part_parent(pcol1))
4 PARTITION BY REFERENCE(ccol1_fk);

Table created.

System Partitioning Method

oracle 11g에서 소개된 partition 방식으로 다른 partition 방식과의 가장 큰 차이는 partition key를 정의하지 않는데 있다. 즉, system이 알아서 partition을 해준다.

CREATE TABLE systab (c1 integer, c2 integer)
PARTITION BY SYSTEM
(
PARTITION p1 TABLESPACE tbs_1,
PARTITION p2 TABLESPACE tbs_2,
PARTITION p3 TABLESPACE tbs_3,
PARTITION p4 TABLESPACE tbs_4
); 

Virtual column-based Partitioning Method

virtual column partition 방식은 partition key의 값이 물리적으로 존재하지 않은 경우에도 virtual column 값에 의해 partition을 할 수 있게 한다.

 CREATE TABLE employees
  (employee_id  number(6) not null, first_name varchar2(30), 
   last_name varchar2(40) not null, email varchar2(25),
   phone_number varchar2(20), hire_date  date not null,
   job_id  varchar2(10) not null, salary number(8,2),
   commission_pct number(2,2), manager_id  number(6),
   department_id number(4),
   total_compensation as (salary *( 1+commission_pct))
   )
    PARTITION BY RANGE (total_compensation)
     (
       PARTITION p1 VALUES LESS THAN (50000),
       PARTITION p2 VALUES LESS THAN (100000),
       PARTITION p3 VALUES LESS THAN (150000),
       PARTITION p4 VALUES LESS THAN (MAXVALUE)
     );






oracle은 데이터베이스 사용자들이 수행한 관련 정보를 SGA라는 메모리에 저장하여 공유합니다.
SGA에는 buffer cache, shared pool, log buffer 등의 많은 구성요소들이 있습니다.



library cache는 shared pool내에서 sql 수행 정보등을 저장하는 가장 중요한 구성요소 중 하나라고 할 수 있습니다.
library cache의 구조는 다음과 같습니다.


SQL이 수행되면 해당 구조에 어떻게 정보가 저장될까요?
sql이 수행되었을 때 library cache내에서 어떻게 저장되는지, LIBRARY_CAHCE dump와 구조를 비교해 보았습니다.
(할일이 없어 그런건 아닙니다 --;)




2009.10.30 update:

library cache 관련해서 좋은 문서가 있어 Link 겁니다.

OWI를 활용한 shared pool 진단 및 튜닝






다음은 oracle korea에서 만든(?) oracle 9i의 new feature인 data block prefetching에 대한 내용을 발췌했습니다..

데이타 블럭 프리페칭(Data Block prefetching)은 Oracle9i에서 새롭게 선보이는 내부 최적화 기능으로서 특정한 경우에 질의 응답 시간을 크게 개선할 수 있다. 데이타 블럭 프리페칭은 테이블 조회(lookup)에 의해 사용된다.

색인 액세스 경로가 선택되고 질의가 색인 만으로는 충족될 수 없을 경우, rowid가 지시하는 데이타 행 역시 페치되어야 한다. 이 데이타 행 액세스(테이블 조회)에 대한 rowid는 올바른 rowid의 어레이가 지시히는 블럭 어레이를 읽는 것을 수반하는 데이타 블럭 프리페칭을 사용해 개선됐다.

블럭 프리페칭은 I/O 처리 성능에 대한 보다 효과적인 활용과 가능할 경우 항상 병렬 읽기를 발행해 응답시간을 단축시킬 수 있도록 지원한다.

데이타 블럭 프리페칭은 대개 색인이 빈약하게 클러스터링되고 테이블 액세스가 전반적으로 낮은 버퍼 캐시 적중률 의 랜덤 디스크 액세스를 수반하는 경우 유용하다.

이 경우 질의는 시스템 상에 가용 I/O 대역폭이 있더라도 이들 단일 블록 들이 캐시로 동시에 읽혀지기를 기다림으로써 손쉽게 I/O 최대값에 도달할 수 있다. 데이타 블럭 프리페칭 주요 색인에 만족스러운 rowid의 수가 축적될 때까지 테이블 블럭의 읽기를 지연시킨다. I/O 완료 시 훨씬 짧게 기다림으로써 데이타베이스 인스턴스가 CPU 자원을 보다 효과적으로  활용할 수 있도록 지 원하게 된다.



샘플 문제:
TPC-H 스펙의 질의 17은 데이타 블럭 프리페치가 사용될 수 있는 대표적인 예제이다.
해당 브랜드 및 컨테이너 유형의 경우, 평균 보다 20% 적은 수량의 주문이 접수된다면, 연평균 매출액 중 총손실액은 어느 정도인가?

데이타베이스는 7년 간의 데이타를 포함하고 있다.

구현:

다음 질의는(DOP 16) Oracle8i and Oracle9i 모두에서 위의 문제를 구현하는데 사용된다

select  sum(l_extendedprice) / 7.0 as avg_yearly parts,
from   parts,  lineitem l1
where  p_partkey = l_partkey
and p_brand = 'Brand#23'
and p_container = 'MED BOX'
and l_quantity <(
                           select 0.2 * avg(l_quantity)
                           from lineitem l2
                           where l_partkey = p_partkey);

Oracle8i에서 질의 수행시간은 264초였다
Oracle9i에서 질의 수행시간은 180초였으며 이는 31.82%의 향상을 나타낸다


다음은 위의 white-paper의 원문입니다.
영어는 잼병이지만, 아래의 원문을 보기전까지는 위의 말이 뭔소린지 이해가 안됬다능..
(한글 공부를 다시해야 하나 .. 고민)

Data block prefetching is an example of an 'under the covers' performance feature provided in Oracle9i. This feature is entirely transparent to both the end-users and database administrator, yet it can improve performance by 30% or more for individual queries.

Data block prefetching can significantly improve the performance of queries which retrieve large numbers of rows via a b-tree index.Data block prefetching provides the largest performance gains for queries which use an index that is 'poorly clustered'. (An index is said to be 'clustered' if the data in the underlying table is in the same order as the index keys. A poorly clustered index is one in which the rows in the underlying table are in an entirely different order than the index keys).

Poorly clustered indexes are quite common, since most tables can only have at most one well-clustered index. Previously, accessing a large number of rows using a poorly clustered b-tree index could be expensive. Each row accessed by the index would likely be in a separate data block and thus would require a separate I/O operation.

The buffer cache is ineffective for these queries because the I/Os are spread throughout the table, and the buffer cache hit ratio is low.

In such cases, a query can easily become I/O bound, waiting for single data blocks to be read into the cache one at a time, even though there may be available I/O bandwidth on the system. With data block prefetching, Oracle delays data blocks reads until multiple rows specified by the underlying index are ready to be accessed and then retrieves multiple data blocks at once, rather than reading a single data block at a time.
Block prefetching allows better utilization of the I/O capacity, and provides a further reduction in response time by issuing I/O operations in parallel whenever possible.


요약하자면 index scan 사용할때 내부적으로 수행되는 기능으로 요구되는 rowid에 의한 access를 일정 block이 될때까지 기다려 한꺼번에 수행하는 기능 입니다.

With data block prefetching, Oracle delays data blocks reads until multiple rows specified by the underlying index are ready to be accessed and then retrieves multiple data blocks at once, rather than reading a single data block at a time.





오라클은 DB link와 synonym을 통해서 remote oracle database에 있는 table을 local database 서버에 있는 것처럼 query가 가능합니다. 하지만 내부적으로는 remote database로 관련 query를 수행해 return 되는 데이터를 이용해 결과 값을 출력해줍니다. (당연하죠..ㅎㅎ) 그러나 remote database의 table에 대한 정보가 없기 때문에 일반적인 role을 이용해 query를 생성, 수행 요청하게 됩니다.

National Hurricane Center
(아무 상관없는 그림이지만 내용이 작아 붙여 봅니다. ^^;)


아래 box 안의 role은 remote database로 query를 수행하는 일반적인 규칙입니다. 참조한 문서가 오래전꺼긴 한데, 큰 차이는 없으리라.. 생각되는 군요.

간단히 보자면 ..

MAX() 같은 group 함수는 전달 되지 않으니, 테이블의 범위에 따라서는 엄청난 데이터가 local로 전송된 후 MAX() 값 등을 구할 수도 있겠군요.

또 상수 조건이 있는 table은 remote로 해당 조건이 전달 되겠지만, 그렇지 않은 경우는 FULL TABLE SCAN을 할 수 도 있겠고요..

ORDER BY도 local에서 수행된다니, 대상이 많으면 이것도 성능에 영향을 줄 수 있겠네요.

  1. Aggregate functions like MAX(), COUNT() are NOT passed across the net but rather are performed on the local node.
  2. Expressions in the SELECT list are decomposed into columns and evaluated when the data is retrieved.
  3. Only a relevant subset of columns are fetched across the net.
  4. An expression in a WHERE clause is passed across to the remote database if it compares the remote column to a literal (eg ename = 'KING').
  5. Expressions in a WHERE clause are passed to the remote database if all columns are in the expression are located in the same remote table the remote database (eg emp.sal * 0.20 > emp.bonus)
  6. Datatype conversion functions like TO_CHAR() in a WHERE clause are subject to the conditions in #4 and #5.
  7. The optimizer only has knowledge of local indexes when making decisions about how to execute a query.
  8. Remote indexes can be still be used to resolve the decomposed query sent to the remote database.
  9. GROUP BY is not sent to the remote database.
  10. ORDER BY is not sent to the remote database.

이 내용은 oracle metalink "Note 1004553.6 DISTRIBUTED QUERY ANALYSIS"를 참조했습니다.

써놓고 보니 DB link를 이용하지 말라는 이야기가 된 것 같아 몇줄 더 씁니다.

물론 group 함수, order by 등의 처리가 local database에서 처리가 되지만 이러한 것들을 피해가는 방법들은 다 있기 마련이죠. view를 만들어 remote에서 group 함수 처리를 한다든지, join의 경우 아예 한 곳에서 처리하게 한다든지, temporary table을 만들어 처리하거나.. 뭐 이런

요는 network을 통한 전달되는 데이터의 양을 얼마나 많이 줄이느냐 겠죠.

위의 metalink note 보시면 몇몇 예제가 있으니 참조하세요.






이 두개의 OPTION은 Precompile된 모든 program의 implicit, exeplicit cursor 에 모두 영향을 준다. (pro*ada의 경우는 약간의 예외가 있다.)

CURSOR에는 program cursor와 oracle cursor 두 가지가 있다.

program cursor는 SQL문으로 인해 생기는 data 구조이다. program cursor는 procompiler에 의해 발견된 각 SQL문 마다 선언된다. 다음의 문장이 program 안에 있다고 하자.

EXEC SQL DECLARE SEL_EMP_CURS CURSOR FOR...
EXEC SQL INSERT...

그렇다면 c1과 c2의 두개의 program cursor가 선언될 것이다.

Oracle cursor ( context area 라고도 한다.)는 실행 중에 생성이 된다. 이 공간은 parse된 문장과, host 변수의 주소값, 그 외에 SQL문을 실행하기 위해 필요한 정보를 가지고 있다.

이 두 개의 cursor는 cursor cache를 통해서 서로 연결되어 있다. 이 cursor cache의 초기 크기는 MAXOPENCURSORS option에 의해 결정이 된다. 아래의 그림은 이러한 연관 관계를 설명한다.

?ui=2&view=att&th=124760a212c92f44&attid=0.1&disp=attd&realattid=ii_124760a212c92f44&zw


이러한 관점에서 HOLD_CURSOR와 RELEASE_CURSOR는 연관 관계는 다음과 같다.

HOLD_CURSOR option는 program cursor와 해당 cache와의 관계를 다룬다. 반면, RELEASE_CURSOR는 Oracle cursor와 cache와의 관계를 다룬다. 만약 자주 재사용되는 SQL문이 보다 빠른 속도를 갖게 하기 위해서는 이 SQL문과 예상되는 Oracle cursor가 붙어 있도록 하는 것이 좋다.

SQL문이 Oracle cursor와 붙어있다는 말은 SQL문과 해당 Oracle cursor 사이의 연결이 지속적으로 유지되고 있는것을 의미한다. 위에서 언급한 바와 같이 Oracle cursor(context area)에서는 parse된 문장이나 host변수의 주소값 등과 같이 중요한 정보가 들어 있게 되므로 SQL문장과 Oracle cursor(context area) 를 붙여두는 것이 유용하다.


이와 같이 문장과 cache를 붙여두기 위해서 HOLD_CURSOR와 RELEASE_CURSOR가 사용된다.

HOLD_CURSOR=YES option이 사용되면 cache들은 재사용이 가능하도록 flag을 표시 할 수 없도록 한다.
이것은 매우 중요한 의미를 가지는데, 만약 cache들이 사용이 되어지고 새로운 SQL문마다 각기 새로운 cache들이 할당이 된다면, MAXOPENCURSORS에 의해 결정된 수만큼의 cache가 할당된 후에 추가의 cache는 재사용이 가능하도 flag가 표시 된 cache를 사용하기 때문이다.

위의 그림을 참조로 예를 들면 다음과 같다.
C(1) cache가 재사용이 사능하도록 표시가 되어있고 EXEC SQL SELECT 문이 실행이 된다고 가정을 할 경우, program cursor P(MAXOPENCURSORS+1)가 생성되고 이것은 cache와 Oracle cursor가 필요하다. 그러나 이때 MAXOPENCURSORS에 의해 결정된 값만큼의 숫자의 cache가 이미 사용되어졌다면, 이 문장은 C(1)의 cache와 거기에 해당하는 Oracle cursor를 할당받는다. 그리고 이 cache과 Oracle cursor는 비워지고 새로운 SQL문으로 다시 parse가 되어진다.


위의 option과 함께 사용되는 것이 RELEASE_CURSOR=NO이다. 이것은 cache들과 Oracle cursor사이의 관계를 규정한다. 이 option은 parse된 문장이 실행하고 난후의 상태를 관리한다. 이때 할당된 memory는 사용가능한 상태로 유지 되어진다. 이 memory를 풀어주기 위해서는 RELEASE_CURSOR=YES를 사용한다. 이 option을 사용 하면 이 cache에 연결되어 있는 다음 문장들은 추가적으로 parse를 다시 해야하는 부담이 있다.



HOLD_CURSOR와 MAXOPENCURSORS는 밀접한 관계를 가진다.

만약 모든 cursor cache들이 현재 "재사용 불가"로 표시되어있다면 ( 이런 경우는 explicit하게 열린 cursor가 close되지 않은 상태로 fetch를 진행하는 경우와 같이 cursor cache를 사용하는 모든 문장이 실행중인 경우와 HOLD_CURSOR option을 사용한 경우등이 있다.),  새로운 cursor를 위해서는 실행중에 cursor cache를 확장해야 한다. (즉,  MAXOPENCURSORS가 10이라면, 11번째의 cursor cache를 생성한다.) 이때 11 번째 생성된 cache는 cursor가 close되어도 제거되지 않는다.

MAXOPENCURSOR를 작게 잡는 것은 memory를 절약 할수있으나 cache가 추가될때는 비용이 많이든다. 반면, 높게 잡을 경우는 추가 비용에 대한 부담이 줄어드는 많큼 필요이상의 memory를 사용하게 된다. 그리고 무조건 가장 오래된 cache를 재사용하도록 허용하는 것이 옳바른 것은 아니다.

만약 10개의 explicit cursor를 선언해서 open한 user가 11번째의 cursor를 사용하기 위하여 가장 오래된 program cursor를 재사용하게 된다면 user는 첫번째 cursor에 대한 위치를 잃어버리게 되어 이 곳에서 fetch를 수행할 수 없게 된다.

만약 program 안에서 문장의 재사용이 일어나지 않을 경우는 HOLD_CURSOR=NO, RELEASE_CURSOR=YES를 사용한다. HOLD_CURSOR=NO 는 cache들이 필요에 따라 자동적으로 "재사용"으로 표시되게 한고, RELEASE_CURSOR=YES는 Oracle cursor 가 자동적으로 해제되고 parse된 문장을 잃어 버리게 한다. site의 memory에 대한 문제로 인해 Oracle cursor들의 숫자가 제한을 받을 경우는 이 option을 사용 해야 한다.

이 때 만약 RELEASE_CURSOR=YES를 사용하게되면 자동적으로 HOLD_CURSOR=YES는 사용할수 없게 된다. RELEASE_CURSOR=YES가 Oracle cursor와 cache사이의 연결 을 끊어 버리고 Oracle cursor를 해제 시켜 버린다. 그러므로 심지어 program cursor가 cache와 HOLD_CURSOR=YES에 의해 연결되어 있어도 memory를 다시 할당하고 다시 parse를 해야 한다.그러므로 RELEASE_CURSOR=YES를 주면 HOLD_CURSOR=YES를 준 이점이 하나도 없다.

이 내용은 oracle discussion forum의 "hold_cursor & release_cursor"와 아래 첨부한 oracle internet seminar 자료, 그리고 oracle metalink의  Note 2055810.6 "Precompiler HOLD_CURSOR and RELEASE_CURSOR Options"를 참고했습니다.
.






oracle 9i rac 이전 버전인 ops에서는 양 노드간의 data sync를 맞추기 위해 file ping 방식을 사용했다. 즉, 한 노드에서 변경된 내용을 다른 노드에서 읽고자 했을 때 변경된 데이터를 file에 write하고 이를 다시 다른 노드에서 읽는 방식을 사용했다.

이 방식은 노드 간에 동일한 데이터를 읽게 되는 경우 file writing이 발생되므로 성능에 심각한 영향을 끼칠 수 있다.
따라서 OPS에서는 양 노드의 업무 분할이 필요했고, 공통으로 사용되는 code성 데이터 사용을 가능한한 최소화 해야 되었다.

oracle 9i RAC에서는 memory간의 통신으로 데이터를 전달하는 cache fusion 방식이 사용되면서 노드간의 데이터 sync 방식의 성능은 크게 향상되었다. 이제 노드간의 데이터 sync는 이전 버전에서 처럼 더이상 심각한 문제가 문제를 유발하지 않으며, 노드간의 업무 분할은 더이상 필요가 없어 졌다.

cache fusion으로 인한 pinging 현상의 성능이 좋아져서 업무 분할이 필요없어짐으로 해서 비로서 load balance 기능 구현이 가능해 졌으며, 이 을 이용해 각 노드의 resource를 보다 효율적으로 사용할 수 있다.

load balance는 client-side load balance와 server-side load balance로 나눌 수있다.

client-side load balance는 tnsnames.ora내의 tns alias 설정시 load balnace 설정에 의해 가능하며 기술된 listener list에 대해 random 하게 connection을 이루게 된다. 따라서 connection의 개수는서버의 resource 사용량이나 기존의 connection 갯수와는 상관없이 random 하게 이루어 진다.

2009년 10월 13일 update :

net_service_name=
 (DESCRIPTION=
  (LOAD_BALANCE=on)
  (ADDRESS=(PROTOCOL=tcp)(HOST=sales1-svr)(PORT=1521))
  (ADDRESS=(PROTOCOL=tcp)(HOST=sales2-svr)(PORT=1521)))
  (CONNECT_DATA=(SERVICE_NAME=sales.us.acme.com))


server-side load balnace는 listener로 들어오는 client connection을 oracle background process인 pmon의 dynamic service registration에 의해 수집된 profile statistic 정보를 이용해 덜 바쁜 노드, 덜 바쁜 instance, 덜 바쁜 dispatcher 순으로 connection을 전이 시켜 준다.

server-side load balance는 init file내의 service_name, local_listener와 remote_listener 등록이 필요하다.

2009년 10월 13일 update :

다음의 그림은 server-side load balancing 구성에 대한 그림이다. instance A는 listener A를 local listener로 갖고 있으며, listener B를 remote listener로 갖는다. 마찬가지로 instance B는 listener B를 local listener로 listener A를 remote listener로 갖는다.

각각의 instance의 PMON process는 주기적으로 해당 node에 대한 profile statistic을 listener에게 알려주어 listener로 들어오는 client connection request는 load가 적은 노드로 connection request를 redirect 하기도 한다.





 
oracle 운영 중 DB hang 이나 slowdown(거의 hang) 현상이 발생하면, 사실 거의 할 수 있는 일이 없다. monitoring이나 trace 등의 데이터는 resource 부족으로 수행이 잘 안되며, 운영 부서에서 빨리 문제 해결해 달라고 엄청 push 하기 마련이다. 대부분 resource 문제는 database restart하면 풀린다. 물론 recovery 등의 작업은 다시 올리면 또 돌긴하지만 이런 작업은 심각한 hang이나 slowdown 현상을 대개 보이지는 않는다.

암튼 조치 후에 문제 분석을 위해서는 반드시 system state dump를 수행해야 한다. 요게 없으면 문제에 대한 기본적인 분석이 어려워 어떤때는 현상 파악 자체도 잘 안되는 경우가 있다. (10g에서는 active session history나 AWR 등의 데이터로 인해 좀 나아졌다)

system state dump는 몇분 주기로 여러번 수행해야 특정 resource의 변동사항을 유추해 낼 수 있다.
일반적으로 system state dump는 두가지의 방법으로 수행된다.

1.  alter session set events 'immediate trace name SYSTEMSTATE level 10';

2.  $ sqlplus (svrmgrl)
      connect internal
      oradebug setospid <process ID>
      oradebug unlimit
      oradebug dump systemstate 10

드물지만 어떤 경우엔 database로의 접근 자체가 안되는 경우가 발생한다(max process 등..). 이럴 경우 당연히 alter session이나 oradebug를 이용해 system dump 수행 자체가 안된다.

이러한 상황에서 system dump state dump를 수행할 수 있는 방법이 있는데, OS의 debug tool을 이용해 직접 dump를 수행하는 function call을 수행하는 방법과 10g에서 소개된 sqlplus의 -prelim option을 사용하는 방법이 그것이다.

OS debuger를 이용해 function call을 하는 경우 조심할 점은 attach 한 process가 terminated 될 수 있으므로 가급적 oracle background process에 대한 수행은 안하는 게 좋다.

dbx -a PID (where PID = any oracle shadow process)
dbx() print ksudss(10)
...return value printed here
dbx() detach

(saki) % dbx -a 28348
Waiting to attach to process 28348 ...
Successfully attached to oracle.
warning: Directory containing oracle could not be determined.
Apply 'use' command to initialize source path.

Type 'help' for help.
reading symbolic information ...
stopped in read at 0xd016fdf0
0xd016fdf0 (read+0x114) 80410014        lwz   r2,0x14(r1)
(dbx) print ksudss(10)
2
(dbx) detach

oracle 10g를 사용하는 경우 사용할 수 있는 option이 하나 있는데, sqlplus의 prelim option이다.
이 방법은 기존의 oradebug를 사용하는 방법과 동일하며 단지 sqlplus 수행시 option으로 지정하면 된다.
 
prelim option에 관한 내용은 10g, 11g sqlplus manual에도 나와 있지 않으므로.. 생략 !!

sqlplus -prelim / as sysdba

export ORACLE_SID=PROD                                 ## Replace PROD with the SID you want to trace
sqlplus -prelim / as sysdba
oradebug setmypid
oradebug unlimit;
oradebug dump systemstate 10

참고한 자료들 입니다.

Note 121779.1 - Taking Systemstate Dumps when You cannot Connect to Oracle
http://oraclue.com/2008/09/25/sqlplus-preliminary-connection/
http://yaping123.wordpress.com/2008/08/30/sqlplus-using-prelim-connection/

2009년 8월 26일 추가.
sqlplus의 relim option 관련해서 참고할 만한 블로그 입니다.

oradeblog : SQL*Plus 'prelim' connection


2009년 10월 09일 추가
-relim option은 정상적인 세션이 아니기 때문에 systemstate dump 명령이 수행이 되더라도 dump가 끝까지 떨어졌는지 확인이 필요합니다. trace file을 여셔서 맨 끝에 "end of system state dump(?)"라고 되어 있는지 확인해 보세요.




+ Recent posts