2009년 11월 27일 금요일

ORACLE_TIP : ROWNUM

오라클 사이트에서 ROWNUM 에 관한 재미있는 문서가 있어서 긁어 왔습니다. 원본을 보고 싶으신 분은 http://www.oracle.com/technology/global/kr/oramag/oracle/06-sep/o56asktom.html 를 클릭하세요.

 

-------------------------------------------------------------

TECHNOLOGY: Ask Tom

ROWNUM의 동작 원리와 활용 방법
저자 - Tom Kyte

오라클 전문가 Tom Kyte가 ROWNUM의 동작 원리와 활용 방법에 대해 설명합니다.

이번 호의 Ask Tom 컬럼은 지금까지와는 조금 다른 내용을 담고 있습니다. 필자는 오라클 데이터베이스에서 Top-N 쿼리와 페이지네이션(pagination) 쿼리를 구현하는 방법에 대해 자주 질문을 받곤 합니다. 하나의 컬럼을 통해 이러한 질문에 한꺼번에 대답하기 위한 방편으로, < Effective Oracle by Design (Oracle Press, 2003)> 의 내용을 인용하기로 했습니다. 컬럼의 포맷에 맞게 책의 내용이 다소 수정되었음을 참고하시기 바랍니다.

결과 셋의 제한

ROWNUM은 오라클 데이터베이스가 제공하는 마술과도 같은 컬럼입니다. 이 때문에 많은 사용자들이 문제를 겪기도 합니다. 하지만 그 원리와 활용 방법을 이해한다면 매우 유용하게 사용할 수 있습니다. 필자는 주로 두 가지 목적으로 ROWNUM을 사용합니다.

  • Top-N 프로세싱: 이 기능은 다른 일부 데이터베이스가 제공하는 LIMIT 구문과 유사합니다.
  • 쿼리 내에서의 페이지네이션(pagination) – 특히 웹과 같은 "stateless" 환경에서 자주 활용됩니다. 필자는 asktom.oracle.com 웹 사이트에서도 이 테크닉을 사용하고 있습니다.

두 가지 활용 방안을 설명하기 전에, 먼저 ROWNUM의 동작 원리에 대해 살펴 보기로 하겠습니다

ROWNUM의 동작 원리

ROWNUM은 쿼리 내에서 사용 가능한 (실제 컬럼이 아닌) 가상 컬럼(pseudocolumn)입니다. ROWNUM에는 숫자 1, 2, 3, 4, ... N의 값이 할당됩니다. 여기서 N 은 ROWNUM과 함께 사용하는 로우의 수를 의미합니다. ROWNUM의 값은 로우에 영구적으로 할당되지 않습니다(이는 사람들이 많이 오해하는 부분이기도 합니다). 테이블의 로우는 숫자와 연계되어 참조될 수 없습니다. 따라서 테이블에서 "row 5"를 요청할 수 있는 방법은 없습니다. "row 5"라는 것은 존재하지 않기 때문입니다.

또 ROWNUM 값이 실제로 할당되는 방법에 대해서도 많은 사람들이 오해를 하고 있습니다. ROWNUM 값은 쿼리의 조건절이 처리되고 난 이후, 그리고 sort, aggregation이 수행되기 이전에 할당됩니다. 또 ROWNUM 값은 할당된 이후에만 증가(increment) 됩니다. 따라서 아래 쿼리는 로우를 반환하지 않습니다.

select * 
  from t 
 where ROWNUM > 1;

첫 번째 로우에 대해 ROWNUM > 1의 조건이 True가 아니기 때문에, ROWNUM은 2로 증가하지 않습니다. 아래와 같은 쿼리를 생각해 봅시다.

select ..., ROWNUM
  from t
 where <where clause>
 group by <columns>
having <having clause>
 order by <columns>;

이 쿼리는 다음과 같은 순서로 처리됩니다.

1. FROM/WHERE 절이 먼저 처리됩니다.
2. ROWNUM이 할당되고 FROM/WHERE 절에서 전달되는 각각의 출력 로우에 대해 증가(increment) 됩니다.
3. SELECT가 적용됩니다.
4. GROUP BY 조건이 적용됩니다.
5. HAVING이 적용됩니다.
6. ORDER BY 조건이 적용됩니다.

따라서 아래와 같은 쿼리는 에러가 발생할 수 밖에 없습니다.

select * 
  from emp 
 where ROWNUM <= 5 
 order by sal desc;

이 쿼리는 가장 높은 연봉을 받는 다섯 명의 직원을 조회하기 위한 Top-N 쿼리로 작성되었습니다. 하지만 실제로 쿼리는 5 개의 레코드를 랜덤하게(조회되는 순서대로) 반환하고 salary를 기준으로 정렬합니다. 이 쿼리를 위해서 사용되는 가상코드(pseudocode)가 아래와 같습니다.

ROWNUM = 1
for x in 
( select * from emp )
loop
    exit when NOT(ROWNUM <= 5)
    OUTPUT record to temp
    ROWNUM = ROWNUM+1
end loop
SORT TEMP

위에서 볼 수 있듯 처음의 5 개 레코드를 가져 온후 바로 sorting이 수행됩니다. 쿼리에서 "WHERE ROWNUM = 5" 또는 "WHERE ROWNUM > 5"와 같은 조건은 의미가 없습니다. 이는 ROWNUM 값이 조건자(predicate) 실행 과정에서 로우에 할당되며, 로우가 WHERE 조건에 의해 처리된 이후에만 increment 되기 때문입니다.

올바르게 작성된 쿼리가 아래와 같습니다.

select *
  from  
( select * 
    from emp 
   order by sal desc ) 
 where ROWNUM <= 5;

위 쿼리는 salary를 기준으로 EMP를 내림차순으로 정렬한 후, 상위의 5 개 레코드(Top-5 레코드)를 반환합니다. 아래에서 다시 설명되겠지만, 오라클 데이터베이스가 실제로 전체 결과 셋을 정렬하지 않습니다. (오라클 데이터베이스는 좀 더 지능적인 방식으로 동작합니다.) 하지만 사용자가 얻는 결과는 동일합니다.

ROWNUM을 이용한 Top-N 쿼리 프로세싱

일반적으로 Top-N 쿼리를 실행하는 사용자는 다소 복잡한 쿼리를 실행하고, 그 결과를 정렬한 뒤 상위의 N 개 로우만을 반환하는 방식을 사용합니다. ROWNUM은 Top- N쿼리를 위해 최적화된 기능을 제공합니다. ROWNUM을 사용하면 대량의 결과 셋을 정렬하는 번거로운 과정을 피할 수 있습니다. 먼저 그 개념을 살펴보고 예제를 통해 설명하기로 하겠습니다.

아래와 같은 쿼리가 있다고 가정해 봅시다.

select ... 
  from ... 
 where ... 
 order by columns;

또 이 쿼리가 반환하는 데이터가 수천 개, 수십만 개, 또는 그 이상에 달한다고 가정해 봅시다. 하지만 사용자가 실제로 관심 있는 것은 상위 N개(Top 10, Top 100)의 값입니다. 이 결과를 얻기 위한 방법에는 두 가지가 있습니다.

  • 클라이언트 애플리케이션에서 쿼리를 실행하고 상위 N 개의 로우만을 가져오도록 명령
  • • 쿼리를 인라인 뷰(inline view)로 활용하고, ROWNUM을 이용하여 결과 셋을 제한 (예: SELECT * FROM (your_query_here) WHERE ROWNUM <= N)

두 번째 접근법은 첫 번째에 비해 월등한 장점을 제공합니다. 그 이유는 두 가지입니다. 첫 번째로, ROWNUM을 사용하면 클라이언트의 부담이 줄어듭니다. 데이터베이스에서 제한된 결과 값만을 전송하기 때문입니다. 두 번째로, 데이터베이스에서 최적화된 프로세싱 방법을 이용하여 Top N 로우를 산출할 수 있습니다. Top-N 쿼리를 실행함으로써, 사용자는 데이터베이스에 추가적인 정보를 전달하게 됩니다. 그 정보란 바로 "나는N 개의 로우에만 관심이 있고, 나머지에 대해서는 관심이 없다"는 메시지입니다. 이제, 정렬(sorting) 작업이 데이터베이스 서버에서 어떤 원리로 실행되는지 설명을 듣고 나면 그 의미를 이해하실 수 있을 것입니다. 샘플 쿼리에 위에서 설명한 두 가지 접근법을 적용해 보기로 합시다.

select * 
  from t 
 order by unindexed_column;

여기서 T가 1백만 개 이상의 레코드를 저장한 큰 테이블이라고, 그리고 각각의 레코드가 100 바이트 이상으로 구성되어 있다고 가정해 봅시다. 그리고 UNINDEXED_COLUMN은 인덱스가 적용되지 않은 컬럼이라고, 또 사용자는 상위 10 개의 로우에만 관심이 있다고 가정하겠습니다. 오라클 데이터베이스는 아래와 같은 순서로 쿼리를 처리합니다.

1. T에 대해 풀 테이블 스캔을 실행합니다.
2. UNINDEXED_COLUMN을 기준으로 T를 정렬합니다. 이 작업은 "full sort"로 진행됩니다.
3. Sort 영역의 메모리가 부족한 경우 임시 익스텐트를 디스크에 스왑하는 작업이 수행됩니다.
4. 임시 익스텐트를 병합하여 상위 10 개의 레코드를 확인합니다.
5.쿼리가 종료되면 임시 익스텐트에 대한 클린업 작업을 수행합니다. .

결과적으로 매우 많은 I/O 작업이 발생합니다. 오라클 데이터베이스가 상위 10 개의 로우를 얻기 위해 전체 테이블을 TEMP 영역으로 복사했을 가능성이 높습니다.

그럼 다음으로, Top-N 쿼리를 오라클 데이터베이스가 개념적으로 어떻게 처리할 수 있는지 살펴 보기로 합시다.

select *
  from 
(select * 
   from t 
  order by unindexed_column)
 where ROWNUM < :N;

오라클 데이터베이스가 위 쿼리를 처리하는 방법이 아래와 같습니다.

1. 앞에서와 마찬가지로 T에 대해 풀-테이블 스캔을 수행합니다(이 과정은 피할 수 없습니다).
2. :N 엘리먼트의 어레이(이 어레이는 메모리에 저장되어 있을 가능성이 높습니다)에서 :N 로우만을 정렬합니다.

상위N 개의 로우는 이 어레이에 정렬된 순서로 입력됩니다. N +1 로우를 가져온 경우, 이 로우를 어레이의 마지막 로우와 비교합니다. 이 로우가 어레이의 N +1 슬롯에 들어가야 하는 것으로 판명되는 경우, 로우는 버려집니다. 그렇지 않은 경우, 로우를 어레이에 추가하여 정렬한 후 기존 로우 중 하나를 삭제합니다. Sort 영역에는 최대 N 개의 로우만이 저장되며, 따라서 1 백만 개의 로우를 정렬하는 대신N 개의 로우만을 정렬하면 됩니다.

이처럼 간단한 개념(어레이의 활용, N개 로우의 정렬)을 이용하여 성능 및 리소스 활용도 면에서 큰 이익을 볼 수 있습니다. (TEMP 공간을 사용하지 않아도 된다는 것을 차치하더라도) 1 백만 개의 로우를 정렬하는 것보다 10 개의 로우를 정렬하는 것이 메모리를 덜 먹는다는 것은 당연합니다.

아래의 테이블 T를 이용하면, 두 가지 접근법이 모두 동일한 결과를 제공하지만 사용되는 리소스는 극적인 차이를 보임을 확인할 수 있습니다.

create table t
as
select dbms_random.value(1,1000000) 
id, 
       rpad('*',40,'*' ) data
  from dual
connect by level <= 100000;

begin
dbms_stats.gather_table_stats
( user, 'T');
end;
/

Now enable tracing, via

exec 
dbms_monitor.session_trace_enable
(waits=>true);

And then run your top-N query with ROWNUM:

select *
  from
(select *
   from t
  order by id)
where rownum <= 10;
 

마지막으로 상위 10 개의 레코드만을 반환하는 쿼리를 실행합니다.

declare
cursor c is
select *
  from t
 order by id;
l_rec c%rowtype;
begin
    open c;
    for i in 1 .. 10
    loop
        fetch c into l_rec;
        exit when c%notfound;
    end loop;
    close c;
end;
/

이 쿼리를 실행한 후, TKPROF를 사용해서 트레이스 결과를 확인할 수 있습니다. 먼저 Top-N 쿼리 수행 후 확인한 트레이스 결과가 Listing 1과 같습니다.

Code Listing 1: ROWNUM을 이용한 Top-N 쿼리

select *
  from
(select *
   from t
  order by id)
where rownum <= 10

call         count     cpu	elapsed   disk     query      current    rows
--------     --------  -------  -------   -------  --------   --------   ------ 
Parse        1         0.00     0.00      0          0        0           0
Execute      1         0.00     0.00      0          0        0           0
Fetch        2         0.04     0.04      0        949        0          10
--------     --------  -------  -------   -------  --------   --------   ------ 
total        4         0.04     0.04      0        949        0          10

Rows                         Row          Source Operation
-----------------            ---------------------------------------------------
10                           COUNT STOPKEY (cr=949 pr=0 pw=0 time=46997 us)
10                           VIEW  (cr=949 pr=0 pw=0 time=46979 us)
10                           SORT ORDER BY STOPKEY (cr=949 pr=0 pw=0 time=46961 us)
100000                       TABLE ACCESS FULL T (cr=949 pr=0 pw=0 time=400066 us)

이 쿼리는 전체 테이블을 읽어 들인 후, SORT ORDER BY STOPKEY 단계를 이용해서 임시 공간에서 사용되는 로우를 10 개로 제한하고 있습니다. 마지막 Row Source Operation 라인을 주목하시기 바랍니다. 쿼리가 949 번의 논리적 I/O를 수행했으며(cr=949), 물리적 읽기/쓰기는 전혀 발생하지 않았고(pr=0, pw=0), 불과 400066 백만 분의 일초 (0.04 초) 밖에 걸리지 않았습니다. 이 결과를 Listing 2의 실행 결과와 비교해 보시기 바랍니다.

Code Listing 2: ROWNUM을 사용하지 않은 쿼리

SELECT * FROM T ORDER BY ID
call         count     cpu	elapsed   disk     query      current    rows
--------     --------  -------  -------   -------  --------   --------   ------ 
Parse         1        0.00     0.00        0        0        0           0
Execute       2        0.00     0.00        0        0        0           0
Fetch        10        0.35     0.40      155      949        6          10
--------     --------  -------  -------   -------  --------   --------   ------ 
total        13        0.36     0.40      155      949        6          10

Rows                         Row          Source Operation
-----------------            ---------------------------------------------------
10                           SORT ORDER BY (cr=949 pr=155 pw=891 time=401610 us)
100000                       TABLE ACCESS FULL T (cr=949 pr=0 pw=0 time=400060 us)

Elapsed times include waiting for the following events:

Event waited on                  Times
------------------------------   ------------
direct path write temp           33
direct path read temp             5

결과가 완전히 다른 것을 확인하실 수 있습니다. "elapsed/CPU time"이 크게 증가했으며, 마지막 Row Source Operation 라인을 보면 그 이유를 이해할 수 있습니다. 정렬 작업은 디스크 상에서 수행되었으며, 물리적 쓰기(physical write) 작업이 "pw=891"회 발생했습니다. 또 다이렉트 경로를 통한 읽기/쓰기 작업이 발생했습니다. (10 개가 아닌) 100,000 개의 레코드가 디스크 상에서 정렬되었으며, 이로 인해 쿼리의 실행 시간과 런타임 리소스가 급증하였습니다.

ROWNUM을 이용한 페이지네이션

필자가 ROWNUM을 가장 즐겨 사용하는 대상이 바로 페이지네이션(pagination)입니다. 필자는 결과 셋의 로우 N 에서 로우 M까지를 가져오기 위해 ROWNUM을 사용합니다. 쿼리의 일반적인 형식이 아래와 같습니다.

select * 
  from ( select /*+ FIRST_ROWS(n) */ 
  a.*, ROWNUM rnum 
      from ( your_query_goes_here, 
      with order by ) a 
      where ROWNUM <= 
      :MAX_ROW_TO_FETCH ) 
where rnum  >= :MIN_ROW_TO_FETCH;

where

여기서,

  • FIRST_ROWS(N)는 옵티마이저에게 "나는 앞부분의 로우에만 관심이 있고, 그 중 N 개를 최대한 빨리 가져오기를 원한다"는 메시지를 전달하는 의미를 갖습니다.
  • :MAX_ROW_TO_FETCH는 결과 셋에서 가져올 마지막 로우로 설정됩니다. 결과 셋에서 50 번째 – 60 번째 로우만을 가져오려 한다면 이 값은 60이 됩니다.
  • :MIN_ROW_TO_FETCH는 결과 셋에서 가져올 첫 번째 로우로 설정됩니다. 결과 셋에서 50 번째 – 60 번째 로우만을 가져오려 한다면 이 값은 50이 됩니다.

이 시나리오는 웹 브라우저를 통해 접속한 사용자가 검색을 마치고 그 결과를 기다리고 있는 상황을 가정하고 있습니다. 따라서 첫 번째 결과 페이지(그리고 이어서 두 번째, 세 번째 결과 페이지)를 최대한 빨리 반환해야 할 것입니다. 쿼리를 자세히 살펴 보면, (처음의 :MAX_ROW_TO_FETCH 로우를 반환하는) Top-N 쿼리가 사용되고 있으며, 따라서 위에서 설명한 최적화된 기능을 이용할 수 있음을 알 수 있습니다. 또 네트워크를 통해 클라이언트가 관심을 갖는 로우만을 반환하며, 조회 대상이 아닌 로우는 네트워크로 전송되지 않습니다.

페이지네이션 쿼리를 사용할 때 주의할 점이 하나 있습니다. ORDER BY 구문은 유니크한 컬럼을 대상으로 적용되어야 합니다. 유니크하지 않은 컬럼 값을 대상으로 정렬을 수행해야 한다면 ORDER BY 조건에 별도의 조건을 추가해 주어야 합니다. 예를 들어 SALARY를 기준으로 100 개의 레코드를 정렬하는 상황에서 100 개의 레코드가 모두 동일한 SALARY 값을 갖는다면, 로우의 수를 20-25 개로 제한하는 것은 의미가 없을 것입니다. 여러 개의 중복된 ID 값을 갖는 작은 테이블을 예로 들어 설명해 보겠습니다.

SQL> create table t
  2  as
  3  select mod(level,5) id, 
     trunc(dbms_random.value(1,100)) data 
  4    from dual
  5  connect by level <= 10000;
Table created.

ID 컬럼을 정렬한 후 148-150 번째 로우, 그리고 148–151 번째 로우를 쿼리해 보겠습니다.

SQL> select *
  2    from
  3  (select a.*, rownum rnum
  4     from
  5  (select id, data
  6     from t
  7   order by id) a
  8   where rownum <= 150
  9  )
 10   where rnum >= 148;

 ID           DATA           RNUM
-------       ----------     -----------
0             38             148
0             64             149
0             53             150

SQL>
SQL> select *
  2    from
  3  (select a.*, rownum rnum
  4     from
  5  (select id, data
  6     from t
  7   order by id) a
  8   where rownum <= 151
  9  )
 10   where rnum >= 148;

 ID           DATA           RNUM
-------       ----------     -----------
0             59             148
0             38             149
0             64             150
0             53             151

로우 148의 경우 DATA=38의 결과가 반환되었습니다. 두 번째 쿼리에서는 DATA=59의 결과가 반환되었습니다. 두 가지 쿼리 모두 올바른 결과를 반환하고 있습니다. 쿼리는 데이터를 ID 기준으로 정렬한 후 앞부분의 147 개 로우를 버린 후 그 다음의 3 개 또는 4 개의 로우를 반환합니다. 하지만 ID에 중복값이 너무 많기 때문에, 쿼리는 항상 동일한 결과를 반환함을 보장할 수 없습니다. 이 문제를 해결하려면 ORDER BY 조건에 유니크한 값을 추가해 주어야 합니다. 위의 경우에는 ROWID를 사용하면 됩니다.

SQL> select *
  2    from
  3  (select a.*, rownum rnum
  4     from
  5  (select id, data
  6     from t
  7   order by id, rowid) a
  8   where rownum <= 150
  9  )
 10   where rnum >= 148;

 ID           DATA           RNUM
-------       ----------     -----------
0             45             148
0             99             149
0             41             150

SQL>
SQL> select *
  2    from
  3  (select a.*, rownum rnum
  4     from
  5  (select id, data
  6     from t
  7   order by id, rowid) a
  8   where rownum <= 151
  9  )
 10   where rnum >= 148;

 ID           DATA           RNUM
-------       ----------     -----------
0             45             148
0             99             149
0             41             150
0             45             151

이제 쿼리를 반복 실행해도 동일한 결과를 보장할 수 있게 되었습니다. ROWID는 테이블 내에서 유니크한 값을 가집니다. 따라서 ORDER BY ID 조건과 ORDER BY ROWID 기준을 함께 사용함으로써 사용자가 기대한 순서대로 페이지네이션 쿼리의 결과를 확인할 수 있습니다.

다음 단계

ASK Tom
오라클 부사장 Tom Kyte가 까다로운 기술적 문제에 대한 답변을 제공해 드립니다. 포럼의 하이라이트 정보를 Tom의 컬럼에서 확인하실 수 있습니다.
asktom.oracle.com

추가 자료:
Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions
Effective Oracle By Design

ROWNUM 개념 정리

지금까지 ROWNUM에 관련하여 아래와 같은 개념을 설명하였습니다.

  • ROWNUM의 할당 원리와 잘못된 쿼리 작성을 피하는 방법
  • ROWNUM이 쿼리 프로세싱에 미치는 영향과 웹 환경의 페이지네이션을 위한 활용 방안
  • ROWNUM을 이용하여 Top N쿼리로 인한 TEMP 공간의 사용을 피하고 쿼리 응답 속도를 개선하는 방법
오라클 오픈월드 행사에 관련하여

이번 오라클 매거진은 오픈월드 특별호로 기획되었습니다. 필자는 이번 행사를 통해 많은 사람들과 직접 대면할 수 있는 기회를 얻게 된 것을 무척 기쁘게 생각하고 있습니다. 행사에 참석하신다면 필자가 진행하는 세션에도 참석해 주실 것을 기대합니다. (필자는 데이터베이스 및 개발에 관련한 세션을 진행할 예정입니다.) 또 OTN이 주최하는 "Meet the Expert" 행사에도 참가할 것입니다. 이미 오래 전부터 오라클 오픈월드 행사에 참여해 왔지만, 일대일 또는 그룹으로 사용자와 만날 수 있다는 것은 언제나 즐거운 경험입니다. 필자가 진행하는 세션과 OTN 이벤트의 스케줄을 한 번 확인해 보시기 바랍니다.

또 필자는 블로그(tkyte.blogspot.com)를 통해 행사 현장의 뉴스와 사진을 제공해 드리고 있습니다. 그 밖에 OTN 에서도 포드캐스트, 동영상, 프리젠테이션 자료와 같은 행사 컨텐트를 다운로드하실 수 있습니다.


Tom Kyte는 1993년부터 오라클에서 일해 왔습니다. 그는 현재 오라클 퍼블릭 섹터 그룹 담당 부사장으로 근무 중이며, Expert Oracle Database Architecture: 9i and 10g Programming Techniques and Solutions (Apress, 2005) , Effective Oracle by Design (Oracle Press, 2003)와 같은 전문서를 집필하였습니다.

2009년 11월 18일 수요일

ORACLE_027. Managing Tables Part - I

Creating Tables

Creating a Table
 다음 구문을 입력하면, admin_emp 라는 이름의 테이블을 admin_tbs 테이블 스페이스의 hr 스키마로 테이블을 생성할 수 있습니다.

   CREATE TABLE   hr.admin_emp (
               empno          NUMBER(5) PRIMARY KEY,
               ename          VARCHAR2(15) NOT NULL,
               job               VARCHAR2(10),
               mgr              NUMBER(5),
               hiredate        DATE DEFAULT (sysdate),
               sal               NUMBER(7,2),
               comm           NUMBER(7,2),
               deptno          NUMBER(3) NOT NULL CONSTRAINT admin_dept_fkey
                                                                    REFERENCE hr.department (department_id) )
   TABLESPACE dmin_tbs
   STORAGE ( INITAIL 50K
                    NEXT    50K
                    MAXEXTENTS 10
                    PCTINCREASE 25 );

Creating a Temporary Table
 임시 테이블을 만드는 것도 가능합니다. 임시 테이블로 정의를 하면 모든 세션에 사용가능하지만 임시 테이블의 데이터는 해당 데이터를 테이블에 넣은 세션의 사용자만 볼 수 있습니다. CREATE GLOBAL TEMPORARY TABLE 구문을 통해서 임시 테이블을 생성할 수 있습니다. ON COMMIT 키워드의 옵션에 따라 트렌젝션 의존적인지 세션 의존적인지를 결정할 수 있습니다.

  • ON COMMIT DELETE ROWS
    트렌젝션이 COMMIT 된 순간 해당 임시 테이블의 모든 행은 삭제됩니다.
  • ON COMMIT PRESERVE ROWS
    세션이 종료될 경우 임시 테이블에 있는 모든 행은 삭제됩니다.

 다음 예제는 트랜젝션 의존적인 임시 테이블을 만드는 방법입니다.

   CREATE GLOBAL TEMPORARY TABLE admin_work_area
               (startdate DATE,
                 enddate DATE,
                 class    CHAR(20))
          ON COMMIT DELETE ROWS;

 임시 테이블에 인덱스가 만들어지기도 합니다. 해당 인덱스도 임시이고 테이블에 있는 데이터에 기반하여 세션이나 트랜젝션이 유효할 동안만 존재합니다.

 

Parallelizing Table Creation
 테이블을 만들때 AS SELECT 단서를 이용할 경우 PARALLEL 구문을 주어 수행할 수 있습니다. CREATE TABLE ... AS SELECT 구문은 두 파트로 이루어져 있습니다. 첫번째는 CREATE 파트(DDL), 두번째는 SELECT 파트(query) 입니다. 오라클은 이 두 파트를 병렬로 수행할 수 있습니다. 다음의 내용중 하나라도 해당사항에 포함된다면 CREATE 파트는 병렬로 수행할 수 있습니다.

  • PARALLEL 단서가 CREATE TABLE ... AS SELECT 구문에 포함 되었을 경우
  • ALTER SESSION FORCE PARALLEL DDL 구문이 정의되었을 경우

 쿼리 파트가 다음 사항을 모두 만족할 경우 병렬로 수행됩니다.

  • 쿼리가 병렬수행의 힌트절(PARALLEL 또는 PARALLEL_INDEX)을 가지고 있을경우나 CREATE 파트가 PARALLEL 단서를 가지고 있을 경우, 또는 스키마 오브젝트가 참조하는 쿼리가 PARALLEL 정의가 되어 있을 경우.
  • 최소 하나의 테이블이 여러개의 파티션을 풀 테이블 스켄하거나 인덱스 범위 스켄을 하도록 지정되어 있을 경우

 테이블을 병렬로 만들 경우 병렬 정의(PARALLEL)가 테이블에 정의됩니다. 어떤 다음 DML 이나 쿼리가 테이블에 사용된다고 하여도 - 병렬수행이 가능하다면 - 병렬로 수행하려 할 것 입니다.

 다음은 병렬수행을 하는 테이블을 만드는 방법입니다.

   CREATE TABLE hr.admin_emp_dept
      PARALLEL
      AS SELECT * FROM hr.employees
      WHERE department_id = 10;

Automatically Collecting Statistics on Tables
 PL/SQL 패키지인 DBMS_STATS 는 코스트 기반의 최적화를 위한 통계정보를 생성하고 관리할 수 있게 해 줍니다. 이 패키지를 이용하여 통계정보를 수집, 변경, 확인, 익스포트, 임포트 그리고 삭제를 할 수 있습니다. 또한 수집된 통계정보에 이름을 부여하여 통계정보를 구분할 수 있습니다.

 CREATE (혹은 ALTER) TABLE 구문에 MONITORING 단서를 달아서 DBMS_STATS 가 자동으로 테이블의 통계정보를 수집할 수 있게 할 수 있습니다.

 모니터링을 해제하기 위해서는 NOMONITORING 단서를 달면 됩니다.

 

 

Altering Tables

 ALTER TABLE 구문을 이용하여 테이블을 변경할 수 있습니다. 테이블을 변경하기 위해 해당 테이블은 해당 스키마에 포함되어 있어야 하거나 오브젝트에 대한 ALTER 권한을 가지거나 ALTER ANY TABLE 시스템 권한이 있어야 합니다.

 테이블을 변경하는 이유는 다양합니다.

  • 물리적인 특성을 변경하기 위해 (PCTFREE, PCTUSED, INITRANS, MAXTRANS, 혹은 스토리지 파라메터)
  • 테이블을 새로운 세그먼트나 새로운 테이블스페이스로의 이동
  • 익스텐트를 할당하거나 사용하지 않는 공간을 해제하기 위해
  • 컬럼의 추가, 이름 변경, 컬럼의 정의를 변경할때 (데이터 타입, 길이, 기본값, NOT NULL 제약조건)
  • 테이블의 로깅 옵션을 변경할때
  • CACHE/NOCACHE 특성을 병경할때
  • 테이블과 연관된 제약조건들을 변경하거나 추가
  • 병렬처리의 DEGREE 값을 변경하기 위해
  • 통계정보의 수집/비수집(MONITORING/NOMONITORING)
  • 테이블명 변경
  • IOT(Index-Organized Table)의 특성 변경하거나 추가
  • 대외 테이블의 특성 변경
  • LOB 컬럼의 변경 혹은 추가
  • 오브젝트 타임의 변경 및 추가

Altering Physical Attributes of a Table

 테이블의 데이터블록 공간을 파라메터(PCTFREE 와 PCTUSED)로 변경시키면 데이터가 들어있는 블록을 보함한 테이블이 소유하고 있는 모든 공간에 대한 설정값이 변경됩니다. 하지만 이미 데이터가 들어있는 데이터 블록의 재정렬 작업은 즉시 일어나진 않고 재정렬이 필요할 시에 재정렬이 수행됩니다.

 테이블의 트랜젝션 엔트리 설정(INITRANS, MAXTRANS)을 변경할때 새로운 INITRANS 값은 새로운 MAXTRANS 값이 테이블의 모든 블록에 적용될동안 순차적으로 적용됩니다.

 INITIAL 과 MINEXTENTS 파라메터는 변경할 수 없습니다. 다른 스토리지 파라메터(NEXT, PCTINCREASE)는 다음번에 추가로 생성되는 테이블의 익스텐트에만 적용이 됩니다. 다음 익스텐트의 크기 할당량은 현재의 NEXT 와 PCTINCREASE 값에 의해 정의되고 첫 생성시의 기본값에 영향을 받지 않습니다.

 

Moving a Table to a New Segment or Tablespace
 ALTER TABLE ... MOVE 구문은 파티션되지 않은 테이블의 데이터를 새로운 세그먼트에 위치시키거나 선택적으로 다른 테이블스페이스에 위치시킬 수 있습니다. 이 문은 또한 ALTER TABLE 구문으로는 변경할 수 없는 테이블 저장소에 관련된 특성을 변경할 수 있게 해 줍니다.

 다음 예제는 hr.admin_emp 테이블을 새로운 세그먼트로 옮기며 새로운 저장공간에 관한 파라메터를 설정합니다.

   ALTER TABLE hr.admin_emp MOVE
     STORAGE ( INITIAL 20K
                      NEXT 40K
                      MINEXTENTS 2
                      MAXEXTENTS 20
                      PCTINCREASE 0 );

 테이블이 LOB 컬럼을 가지고 있을시 사용자가 정의해 줄 경우에 LOB 데이터나 LOB 인덱스를 새로운 세그먼트로 옮길 수 있습니다. 정의하지 않으면 LOB 데이터와 LOB 인덱스는 옮겨지지 않습니다.

 

Manually Allocating Storage for a Table
 오라클은 필요할시에 테이블에 새로운 익스텐트를 동적으로 추가합니다. 하지만 사용자가 지금 당장 추가적으로 익스텐트를 할당할 수 있습니다.  새로운 익스텐트는 ALTER TABLE ... ALLOCATE EXTENT 구문을 통해 할당받습니다.  사용하지 않는 공간에 대한 익스텐트를 제거하기 위해서는 ATLER TABLE .. DEALLOCATE UNSED 구문을 사용하여 공간을 해제합니다.

 

Modifying an Existing Column's Definition
 ALTER TABLE ... MODIFY 구문을 통해 기 존재하는 컬럼의 데이터 타입, 기본값 혹은 제약조건등의 정의를 변경할 수 있습니다.

 또한 컬럼의 새로운 크기가 기존에 존재하는 컬럼의 데이터 크기를 만족한다면 컬럼의 크기 역시 바꿀 수 있습니다. 비어있지 않은 CHAR 컬럼의 크기를 조절하기 위해서는 BLANK_TRIMMING=TRUE 초기화 파라미터가 설정되어 있어야 합니다.

 CHAR 컬럼의 크기를 증가시키는 것은 시간을 요구하는 명령이며 추가적인 공간이 요구될 수도 있는 작업이며 특히 많은 행을 가진 테이블에서 그렇습니다. 이는 각 행의 CHAR 의 값이 새로운 컬럼의 길이를 맞추기 위한 빈 공간이 삽입되기 때문입니다.

 

Adding Table Columns
 기 존재하는 테이블에 컬럼을 추가하기 위해 ALTER TABLE ... ADD 구문을 사용합니다. 다음 구문은 hr.admin_emp 테이블에 bonus 컬럼을 추가하는 명령입니다.

   ALTER TABLE hr.admin_emp ADD (bonus NUMBER(7,2));

 테이블에 새로운 컬럼이 추가될시 DEFAULT 단서를 달지 않았다면 초기 값은 NULL 입니다. 기본값을 정의하면서 새로운 컬럼을 만들면 각 행에 기본값을 넣으며 컬럼을 추가합니다.

 테이블이 아무 행도 가지고 있지 않거나 기본값을 정의했다면 NOT NULL 제약조건을 사용할 수 있습니다.

 

Renaming Table Columns
 테이블이 이미 존재하는 컬럼의 이름을 바꿀 수 있습니다. ALTER TABLE .. RENAME COLUMN 구문을 이용하면 됩니다. 새로운 컬럼명은 이미 존재하는 다른 컬럼들과 이름이 겹쳐서는 안됩니다. RENAME COLUMN 에 추가적으로 달리는 명령은 없습니다.

  다음 예제는 hr.admin_emp 테이블의 comm 컬럼명을 바꾸는 방법입니다.

   ALTER TABLE hr.admin_emp RENAME COLUMN comm TO commission;

  테이블의 컬럼명을 바꾸는 것은 해당 컬럼을 참조하는 의존적인 오브젝트를 무효로 만들 수 있습니다만 컬럼명을 바꿀시 오라클은 function-based indexes 와 check constraints 는 유효상태로 두기위해 딕셔너리 테이블은 업데이트를 합니다.

 

Dropping Table Columns
 테이블이나 IOT (Index-organized Table)에서 더이상 필요하지 않은 컬럼이 있다면 삭제할 수 있습니다. 이는 데이터베이스의 공간 관리를 편하게 해 주며, 데이터를 익스포트/임포트하고 인덱스와 제약조건을 다시 만드는 행위를 막아줍니다.

 SYS 가 소유하고 있는 테이블의 컬럼이나 한 테이블의 모든 컬럼을 제거할 수는 없습니다.

 

Removing Columns from table
 ALTER TABLE ... DROP COLUMN 구문을 수행시 테이블의 각 행에서 컬럼에 대한 기술과 연관된 데이터는 삭제됩니다. 또한 한번에 여러개의 컬럼을 지울 수 있습니다. 다음 예제는 hr.admin_emp 테이블에서 컬럼을 제거하는 방법입니다.

   ALTER TABLE hr.admin_emp DROP COLUMN sal;

 다음 예제는 bonus 와 comm 컬럼을 한번에 삭제하는 방법입니다.

   ALTER TABLE hr.admin_emp DROP COLUMN (bonus, comm);

Marking Columns Unused
 큰 테이블에서 컬럼을 삭제하느라 시간이 오래 걸릴것이 걱정된다면 ALTER TABLE .. SET UNSED 구문을 사용하여 해결할 수 있습니다. 이 구문은 해당 컬럼을 사용하지 못하게 표시를 해 놓지만 데이터는 삭제되지 않고 이들이 사용하고 있는 공간을 해제하지도 않습니다. 하지만 표시된 컬럼은 더이상 쿼리나 데이터 딕셔너리 뷰를 통해 확인할 수 없으며 새롭게 만들게 되는 컬럼이 해당 이름을 사용할 수 있게 됩니다. 해당 컬럼에 속한 모든 제약조건, 인덱스, 그리고 통계정보 역시 삭제됩니다.

 hiredate 와 mgr 컬럼을 사용하지 않는 컬럼으로 설정하기 위해 다음 방법을 사용하면 됩니다.

   ALTER TABLE hr.admin_emp SET UNSED (hiredate, mgr);

 후에 표시된 컬럼을 삭제하기 위해 ALTER TABLE .. DROP UNSED COLUMNS 구문을 통해 수행할 수 있습니다.

 USER_UNUSED_COL_TABS, ALL_UNSED_COL_TABS, DBA_UNSED_COL_TABS 뷰를 통해 테이블에 사용하지 않게 표시된 컬럼이 몇개 있는지 확인할 수 있습니다. COUNT 필드는 해당 테이블의 사용하지 않는 컬럼 갯수 입니다.

Removing Unsed Columns
 ALTER TABLE .. DROP UNUSED COLUMNS 은 오직 UNUSED 표시가 되어 있는 컬럼에만 적용됩니다. 물리적으로 테이블의 컬럼을 삭제하며 차지하고 있던 공간도 반환됩니다.

 다음 예제는 CHECKPOINT 단서가 달려 있습니다. 이 옵션은 정의된 숫자만큼의 행에서 컬럼 데이터가 삭제되었을시 체크포인트가 발생하게 되며 이 예제에서는 250개의 행마다 수행후 체크포인트가 발생됩니다. 체크포인트를 통해 언도 스페이스의 공간이 전부 소진되는 것을 막으며 언두 로그의 크기를 줄이는 역할을 합니다.

   ALTER TABLE hr.admin_emp DROP UNUSED COLUMNS CHECKPOINT 250;

 

 

Redefining Tables Online

 때때론 상당히 크고 자주 사용하는 테이블들의 쿼리 성능을 향상시키거나 이들에게 DML 작업을 수행해야 할 때가 있습니다. 오라클은 이러한 테이블들의 정의를 온라인 상에서 변경할 수 있는 메커니즘을 제공하고 있습니다.

The DBMS_REDEFINITION Package
 온라인상에서 테이블을 재정의 하는 방법은 DBMS_REDEFINITION PL/SQL 패키지를 사용하는 방법 입니다. 이 패키지를 수행하기 위해서는 EXECUTE_CATALOG_ROLE 권한이 있어야 하며 더불어 다음의 권한 역시 요구됩니다.

  • CREATE ANY TABLE
  • ALTER ANY TABLE
  • DROP ANY TABLE
  • LOCK ANY TABLE
  • SELECT ANY TABLE

Steps for Online Redifinition of Tables
 온라인 상에서 테이블 정의를 변경하기 위해서는 다음 순서를 따릅니다.
  1. 재정의 하는 두가지 방법중 다음 한가지를 선택합니다.

    • 첫번째 방법은 재정의를 하는데 프라이머리 키를 사용하는 방법입니다. 이 방법은 테이블의 재정의 전후와 비교하여 같은 프라이머리 키 컬럼을 가지고 있게 됩니다. 이 방법은 재정의의 기본 방법입니다.

    • 재정의의 두번째 방법은 Rowid를 이용하는 방법입니다. 이 방법은 IOT(Index Organized Table)이 아닐 경우에 사용할 수 있습니다. 또한 이 방법은 숨겨진 컬럼(M_ROW$$) 이 추가되며 테이블 재정의가 끝난후 이 컬럼을 UNSED 로 설정하거나 아니면 삭제하기를 권장합니다.

  2. DBMS_REDEFINITION.CAN_REDEF_TABLE() 프로시저를 이용하여 온라인 상에서 테이블을 변경하거나 재정의 하는 방법을 확인하도록 하십시요. 만약 해당 테이블이 온라인상에서 변경할 수 없는 테이블이라면 왜 온라인상에서 테이블을 변경할 수 없는지를 설명하는 에러 메세지가 출력될 것 입니다.

  3. 변경하고자 했던 테이블의 속성을 가진 (재정의 될 테이블과 같은 스키마로)임시 테이블을 만듭니다. 만약 재정의 될 테이블중 드롭될 컬럼이 있다면, 해당 컬럼은 임시 테이블에 만들지 않습니다. 컬럼이 추가된다면 임시 테이블에도 추가될 컬럼을 포함하여 생성하도록 합니다.

  4. DBMS_REDEFINITION.START_REDEF_TABLE() 를 다음의 값을 주어 호출하여 테이블 재정의를 시작합니다.

    • 재정의될 테이블
    • 임시 테이블
    • 컬럼 맵핑
    • 재정의 수단

    만약 컬럼 매핑정보를 제공하지 않으면 모든 컬럼(기존의 컬럼명 그대로) 이 임시 테이블에 포함되어 있어야 합니다. 컬럼 매핑 정보를 제공한다면 명확히 매핑된 컬럼에 대해서만 재정의 작업이 수행됩니다. 재정의 수단을 제공하지 않으면 기본적으로 프라이머리 키를 이용한 재정의 작업을 수행하게 됩니다.

  5. 트리거, 인덱스, 권한, 제약조건을 임시 테이블에 작성합니다. 임시 테이블에 포함된 어떤 제약조건이라도 반드시 Disable 상태로 만들어야 합니다. 재정의 과정이 성공하든 실패하든 임시 테이블에 존재하는 어떤 트리거들도 수행되지 않습니다.

    재정의가 완료되면 임시테이블의 트리거, 제약조건, 인덱스, 그리고 권한을 재정의 된 테이블에 대체시킵니다. 임시테이블의 참조 제약조건은 재정의된 테이블에 전송하고 해당 제약조건들을 Enable 시킵니다.

  6. DBMS_REDEFINITION.FINISH_REDEF_TABLE() 프로시저를 호출하여 테이블의 재정의를 완료합니다. 이 과정동안 원래의 테이블은 Exclusive Lock 을 걸게 됩니다. 이 과정에서 다음과 같은 일이 발생하게 됩니다.

    • 원래의 테이블은 임시 테이블의 특성, 인덱스, 제약조건, 권한 그리고 트리거와 같이 재정의 됩니다.
    • 임시 테이블의 제약조건들은 이제 재정의된 테이블도 해당 제약조건을 가지고 있으며 활성된 상태로 있습니다.

  7. 선택적으로 변경된 임시테이블의 인덱스, 트리거, 제약조건들은 재정의된 테이블에 함께 적용됩니다. ROWID를 이용하여 재정의를 했으면 재정의된 테이블은 숨겨진 컬럼(M_ROW$$)를 가지게 되고 이 숨겨진 컬럼은 다음과 같은 방법으로 사용하지 않게 마킹에 해두는 것을 권장합니다.

    ALTER TABLE table_name SET UNUSED (M_ROW$$);

  8. 다음은 재정의 과정의 마지막 결과 입니다.

    • 원본 테이블의 형식과 모양은 임시 테이블과 같이 재 정의 됩니다.
    • 트리거, 권한, 인덱스, 그리고 제약조건은 START_REDEF_TABLE() 이후에 정의되고 FINISH_REDEF_TABLE() 이전에 재정의될 테이블에 정의됩니다. 재정의 과정 전에 있던 임시 테이블의 참조 제약조건은 재정의 된 테이블에 적용되며 활성화 됩니다.
    • 기존 재정의 되기 전의 원본 테이블이 가지고 있는 인덱스, 권한, 트리거, 제약조건등은 임시 테이블에 그대로 정의되며 사용자가 임시 테이블을 삭제할때 같이 제거됩니다. 원래의 제약조건등은 임시 테이블에 존재하게 되며, 비활성화 된 상태로 있게 됩니다.
    • 재정의 전의 원본 테이블에 정의되 있던 PL/SQL 프로시저나 커서는 더이상 사용할 수 없습니다. 이들은 다음 언젠가 수행하기 전 자동으로 재확인 하게 됩니다. (재정의 과정의 결과로 테이블의 모양이 바뀌게 되면 재확인 절차는 실패하게 됩니다.)

 

Intermediate Syncronization

 START_REDEF_TABLE() 을 호출하여 재정의 과정을 수행 후 FINISH_REDEF_TABLE() 이 호출되기 전까지 많은 수의 DML 명령이 원본 테이블에 수행될 수 있습니다. 이러한 경우 임시 테이블을 원본 테이블과 주기적으로 동기화를 시켜주는 것을 권장합니다. 이 방법은 DBMS_REDEFINITION.SYNC_INTERM_TABLE() 프로시저를 통해 수행할 수 있습니다. 이 프로시저를 호출함으로써 FINISH_REDEF_TABLE()을 호출하여 재정의를 마치는 사이의 시간을 절약할 수 있습니다.

Abort and Cleanup After Errors
 재정의 과정에서 에러가 발생하게 되거나 재정의 작업을 취소하고 싶다면 DBMS_REDEFINITION.ABORT_REDEF_TABLE() 프로시저를 호출할 수 있습니다. 이 프로시저는 임시 로그와 재정의 작업과 관련된 테이블들을 제거합니다. 이 프로시저가 호출된 후 임시 테이블, 임시 테이블과 연관된 오브젝트들을 삭제할 수 있습니다.

 

Example of Online Table Redefinition

 다음 예제는 empno, ename, job, deptno 컬럼을 가진 hr.admin_emp 테이블을 온라인중에 테이블을 재정의 하는 과정입니다. 이 테이블은 다음과 같이 재정의 됩니다.

  • mgr, hiredate, sal, bonus 컬럼의 추가
  • 새로운 컬럼은 0으로 초기화
  • deptno 컬럼은 10 단위로 증가
  • 재정의된 테이블은 empno 기준으로 파티션됨

 이 과정은 다음과 같습니다.

  1. 해당 테이블이 온라인상에서 재정의가 가능한지 검증합니다.

    BEGIN
    DBMS_REDEFINITION.CAN_REDEF_TABLE('hr','admin_emp',dbms_redefinition.cons_use_pk);
    END;
    /
  2. 임시 테이블 hr.int_amdin_emp 를 만듭니다.

    CREATE TABLE hr.int_admin_emp
        (empno          NUMBER(5) PRIMARY KEY,
        ename          VARCHAR2(15) NOT NULL,
        job               VARCHAR2(10),
        mgr              NUMBER(5),
        hiredate        DATE DEFAULT (sysdate),
        sal               NUMBER(7,2),
        deptno          NUMBER(3) NOT NULL,
        bonus           NUMBER(7,2) DEFAULT (1000))
     PARTITION BY RANGE(empno)
        (PARTITION emp1000 VALUES LESS THAN (1000) TABLESPACE admin_tbs,
         PARTITION emp2000 VALUES LESS THAN (2000) TABLESPACE admin_tbs2)
  3. 재정의 과정을 시작합니다.

    BEGIN
    DBMS_REDEFINITION.START_REDEF_TABLE
     ('hr','admin_emp','int_admin_emp',
      'empno empno, ename ename, job job, deptno+10 deptno,0 bonus',dbms_redefinition.cons_use_pk);
    END;
    /
  4. hr.int_admin_emp 에 트리거, 인덱스, 제약조건을 만듭니다. 재정의의 마지막 과정에서 이것들은 원본 테이블로 전송됩니다. hr.int_admin_emp 테이블의 제약조건들은 비활성 상태여야 합니다. 임시테이블과 연관된 어던 권한도 줄 수 있습니다. 재정의가 끝나면 원본 테이블에 권한을 덮어 씁니다.

    ALTER TABLE hr.int_admin_emp ADD CONSTRAINT admin_dept_fkey2
          FOREIGN KEY (deptno) REFERRENCE hr.departments (department_id);
    ALTER TABLE hr.int_admin_emp MODIFY CONSTRAINT admin_dept_fkey2
          DISABLE KEEP INDEX;

    비활성된 admin_dept_fkey2 제약조건은 재정의 과정의 마지막에 자동으로 활성화 되며 admin_emp 테이블에 적용됩니다.
  5. 선택적으로 임시 테이블 hr.int_admin_emp 에 동기화 작업을 수행할 수 있습니다.

    BEGIN
    DBMS_REDEFINITION.SYNC_INTERM_TABLE('hr','admin_emp','int_admin_emp');
    END;
    /
  6. 재정의 완료

    BEGIN
    DBMS_REDEFINITION.FINISH_REDEF_TABLE('hr','admin_emp','int_admin_emp');
    END;
    /
  7. 임시 테이블 삭제

Open Restrictions

 

 

Continue_

2009년 11월 5일 목요일

ORACLE_026. Managing Datafiles

Guidelines for Managing Datafiles

 데이터파일이란 데이터베이스의 논리적 구조와 모든 데이터가 저장되어 있는 운영체제의 물리적인 파일을 뜻합니다. 오라클은 각각의 데이터파일에 절대번호(absolute)와 상대번호(relative)를 부여하여 각각의 식별자로 사용합니다.

  • Absolute
    데이터베이스에서 유일한 번호를 부여합니다. 이전 버전에서는 절대번호는 간단히 "파일 번호"로 써 참조하였습니다.
  • Relative
    테이블스페이스에서 데이터파일을 구분하기 위한 유일번호 입니다. 중소형 데이터베이스에서 상대번호는 종종 절대번호와 같은 값을 가지기도 합니다. 하지만 데이터베이스의 데이터파일이 거진 최대값에 다다르게 되면 (일반적으로 1023) 상대번호는 절대번호와 다른 값을 가지게 됩니다.

 파일 번호는 많은 딕셔너리 뷰에서 확인 할 수 있습니다. SQL 구문에서 파일 번호는 파일 이름 대신으로 사용할 수도 있습니다. 파일 번호를 사용할때 파일 번호의 정의는 V$DATAFILE, V$TEMPFILE 뷰에서 FILE# 컬럼에 되어 있습니다. 이 파일 번호는 또한 DBA_DATA_FILES, DBA_TEMP_FILES 뷰의 FILE_ID 컬럼에서도 확인 가능합니다.

 

Determine the Number of Datafiles
 최소한 데이터베이스에는 SYSTEM 테이블스페이스를 저장할 하나의 데이터 파일이 필요합니다. 작은 시스템에서는 하나의 데이터 파일만 가질 것 입니다.

 

Determine the Value of the DB_FILES Initialization Parameter
 오라클 인스턴스가 시작될때  인스턴스가 만들 수 있는 최대 데이터 파일 갯수는 DB_FILES 초기화 파라메터가 SGA의 공간에 데이터 파일의 최대 갯수를 미리 할당하게 합니다. DB_FILES 파라메터 값을 변경할 수 있습니다만 새로운 값은 인스턴스가 재시작 되기 전까지 적용되지 않습니다.
 DB_FILES 값을 정의할때 다음 사항을 고려하도록 하십시요.

  • DB_FILES 값이 너무 낮을경우 DB_FILES 값을 늘린후 인스턴스를 재가동 하기 전까지는 데이터 파일을 만들 수 없습니다.
  • DB_FILES 값이 너무 높을경우, 쓸데없이 메모리를 많이 차지하게 됩니다.

Limitations When Adding Datafiles to a Tablespace
 다음 제한사항 아래 테이블스페이스에 데이터파일을 추가할 수 있습니다.

  • 운영체제는 종종 한번에 열 수 있는 파일의 갯수를 제한하기도 합니다. 운영체제의 최대 파일 열기 갯수를 초과할 경우 데이터파일은 더이상 생성되지 않습니다.
  • 운영체제는 데이터파일의 갯수와 크기를 제한합니다.
  • 오라클은 어떤 오라클데이터베이스의 어떤 인스턴스라도 데이터파일 갯수의 제한을 가지고 있습니다. 이 제한은 운영체제가 설정한 제한값입니다.
  • DB_FILES 초기화 파라메터에 설정된 값 보다 더 많은 수의 데이터파일을 만들 수 없습니다.

Consider the Performance Impact
 수개의 데이터파일이 하나의 테이블스페이스를 포함하고 있다면 성능에 문제를 야기할 수 있습니다.

 오라클은 운영체자가 제한하는 파일 갯수보다 좀더 많은 파일을 가질 수 있으며. DBWn 프로세스는 온라인된 모든 데이터 파일을 열 수 있습니다. 오라클은 오픈 파일 디스크립터(open file discriptor)를 캐쉬처럼 사용하며 오픈 파일 디스크립터가 운영체제의 제한에 닿게 되면 자동으로 파일을 닫습니다. 이는 데이터베이스의 성능에 악영향을 미칩니다.

 

Determine the Size of Datafiles
 첫번째 데이터 파일은 (시스템 테이블스페이스) 반드시 최소 150메가가 되어야 데이터 딕셔너리를 초기화 하고 롤백 세그먼트를 생성할 수 있습니다.

 

Places Datafiles Appropriately
 테이블 스페이스의 위치는 테이블 스페이스를 구성하는 데이터파일의 물리적인 위치에 의해 결정됩니다.

 예를들어, 몇몇의 디스크가 데이터베이스를 저장할 수 있다면, 다른 디스크가 데이터파일을 저장할 수 있게 고려하도록 하십시요. 이 방법을 사용하면 사용자들이 정보를 얻기위한 쿼리를 수행시 각각의 디스크가 각자 일을하며, 같은 시간에 데이터를 반환할 수 있습니다.

 

Store Datafiles Spearate from Redo Log Files
 데이터파일이 저장되는 위치는 리두로그 파일이 저장되는 디스크와 절대 같이 저장하지 말기를 권고합니다. 두 데이터파일이 한 디스크에 같이 저장되고 있을때 디스크 에러가 발생하면 그 파일은 데이터베이스 복구에 사용될 수 없습니다.

 리두 로그파일을 멀티플렉싱으로 구성했을시 모든 리두로그 파일을 잃을 가능성은 매우 낮습니다. 따라서 같은 드라이브에 이 경우는 데이터파일을 몇몇의 리두 로그 파일과 같이 저장할 수 있습니다.

 

 

Creating Datafiles and Adding Datafiles to Tablespace

 테이블스페이스를 작성시 오브젝트에 대한 크기를 계산하여야 하고 특정 데이터 파일을 만들어 주어야 합니다. 후에 필요시 데이터 파일을 추가함으로써 디스크에 테이블 스페이스의 크기를 좀더 할당할 수 있습니다.  데이터파일을 여러개의 드라이브에 나눠서 저장하는 방식을 권장합니다.

  • CREATE TABLESPACE
    테이블 스페이스와 포함되는 데이터파일을 만듭니다.
  • CREATE TEMPORARY TABLESPACE
    LMT 방식의 임시 테이블스페이스를 만들며 포함되는 데이터파일을 만듭ㄴ디ㅏ.
  • ALTER TABLESPACE ... ADD DATAFILE
    테이블스페이스에 데이터파일을 만들고 추가시킵니다.
  • ALTER TABLESPACE ... ADD TEMPFILE
    임시 테이블스페이스에 데이터파일을 만들고 추가시킵니다.
  • CREATE DATABASE
    데이터베이스를 만들고 연관된 데이터파일을 작성합니다.
  • ALTER DATABASE ... CREATE DATAFILE
    백업하지 않은 데이터파일의 분실시 비어있는 새로운 데이터 파일을 작성후 새로운 데이터 파일에 연동시킵니다. 

 

Changing a Datafile's Size

Enabling and Disabling Automatic Extention for a Datafile
 데이터 파일을 새로 작성하거나 기존에 있는 데이터파일을 변경하여 데이터베이스에서 좀더 용량이 필요할때 자동으로 용량이 늘어나게 할 수 있습니다.

 자동으로 크기를 확장시키게 하면 다음과 같은 이점이 있습니다.

  • 테이블스페이스의 공간부족시 즉시 사용자가 이를 해결해야하는 수고로움을 버릴 수 있습니다.
  • 익스텐트를 확보하지 못해 어플리케이션이 죽는 일이 없습니다.

 데이터파일이 자동확장을 하는지 아닌지는 DBA_DATA_FILES뷰의 AUTOEXTENSIBLE 컬럼을 참조하면 됩니다.

 자동확장을 사용하려면 다음과 같은 SQL 구문을 사용하여 데이터 파일을 만들때 적용시킬 수 있습니다.

  • CREATE DATABASE
  • CREATE TABLESPACE
  • ALTER TABLESPACE

 기존에 있는 데이터파일의 자동확장을 키거나 끌 수 있습니다. 혹은 ALTER TABLESAPCE 구문을 통해 수동으로 크기를 조절할 수도 있습니다.

 다음 구문은 users 테이블에 데이터파일을 추가하며 해당 데이터파일이 자동 확장을 할 수 있게 하는 구문입니다.

  ALTER TABLESPACE users ADD DATAFILE '/u02/oracle/rbdb1/users03.dbf' SIZE 10M
    AUTOEXTEND ON
    NEXT 512K
    MAXSIZE 250M;

 NEXT 옵션의 값은 데이터파일 확장시 늘어나는 최소 용량을 뜻합니다. MAXSIZE는 해당 데이터파일이 늘어날 수 있는 최대 용량을 뜻합니다.  자동 확장 기능을 끄기 위해서는 다음의 구문을 사용합니다.

 

  ALTER DATABASE DATAFILE '/u02/oracle/rbdb1/users03.dbf' AUTOEXTEND OFF;

 

Manually Resizing a Datafile
 ALTER DATABASE 구문을 이용하여 수동으로 데이터파일을 늘리거나 줄일 수 있습니다.

 데이터파일의 크기를 재조정함으로써 데이터파일을 추가하지 않고 데이터베이스에 좀더 많은 공간을 확보할 수 있게 합니다. 이 방법은 데이터베이스가 사용하는 데이터파일의 갯수가 최대치에 근접해 있을 경우 사용하면 좋습니다.

 수동으로 크기를 줄일 경우 사용하지 않는 데이베이스의 빈 공간을 반환할 수 있습니다.

 다음 예제에서 '/u02/oracle/rbdb1/stuff01.dbf' 파일이 250M 까지 확장하였다고 가정합니다. 하지만 테이블스페이스가 작아졌을 경우 데이타파일의 크기를 줄일 수 있습니다.

 다음 구문을 통해 해당 데이터파일의 크기를 줄여봅시다.

  ALTER DATABASE DATAFILE '/u02/oracle/rbdb1/stuff01.dbf' RESIZE 100M

 

 

Altering Datafile Availability

Bringing Datafiles Online or Taking Offline in ARCHIVELOG Mode
 
개인 데이터파일을 온라인시키려면 ALTER DATABASE DATAFILE 구문을 이용합니다. 다음 구문은 특정한 데이터파일을 온라인 시키는 명령 입니다.

  ALTER DATABASE DATAFILE '/u02/oracle/rbdb1/stuff01.dbf' ONLINE;

 반대로 오프라인 시키려면 다음 명령을 수행합니다.

  ALTER DATABASE DATAFILE '/u02/oracle/rbdb1/stuff01.dbf' OFFLINE;

 * 이 형식의 ALTER DATABASE 구문을 사용하기 위해 반드시 ARCHIVELOG 여야 합니다. 이는 데이터파일이 OFFLINE 될때 생기는 사고로 데이터파일이 손상되는 것을 막기 위합입니다. NOARCHIVELOG 상태에서 수행시 데이터파일에 손상이 됬다면 데이터파일을 잃는 결과를 초래할 수 있습니다.

 

Taking Datafiles Offline in NOARCHIVELOG Mode
 NOARCHIVELOG 상태에서 데이터파일을 오프라인 시킬때도 ALTER DATABASE DATAFILE ... OFFLINE DROP 구문을 사용합니다. 이는 데이터파일을 오프라인시키고 즉시 드롭할 수 있게 해 줍니다. 데이터파일이 임시 세그먼트의 데이터만 가지고 있고 아직 백업이 되지 않은 상태며 데이터베이스가 NOARCHIVELOG 일때 유용합니다.

  ALTER DATABASE DATAFILE '/u02/oracle/rbdb1/stuff01.dbf' OFFLINE DROP;

 

Altering the Availability of All Datafiles and Tempfiles in Tablespace
 ALTER TABLESPACE 구문을 이용하여 테이블스페이스가 가진 데이터파일을 온라인 혹은 오프라인 시킬 수 있습니다.

  • ALTER TABLESPACE ... DATAFILE {ONLINE | OFFLINE}
  • ALTER TABLESPACE ... TEMPFILE {ONLINE | OFFLINE}

대부분의 경우 위 ALTER TABLESPACE 구문은 데이터베이스가 오픈상태가 아닌 마운트 상태에서 수행할 수 있습니다. 다만 해당 테이블스페이스가 시스템 테이블스페이스, 언두 테이블스페이스, 혹은 기본 임시 테이블 스페이스일 경우 반드시 마운트 상태에서만 수행해야 합니다. ALTER DATABASE DATAFILE 과 ALTER DATABASE TEMPFILE 구문 역시 ONLINE/OFFLINE 옵션을 가지고 있습니다. 다만 이 경우의 구문은 테이블스페이스의 파일이름을 전부 적어주어야 합니다.

 테이블스페이스의 상태를 변경하는 ALTER TABLESPACE .... ONLINE | OFFLINE 과의 다른 이유는 다른 행동을 취하기 때문입니다. ALTER TABLESPACE 구문은 해당 테이블이 가진 모든 데이터파일에 유효하지만 임시 테이블스페이스나 템프파일의 상태는 변경할 수 없습니다.

 

 

Renaming and Relocating Datafiles
Renaming and Relocating Datafiles for a Single Tablespace
 하나의 테이블스페이스에 포함된 데이터 파일의 이름 혹은 위치를 변경시킬 수 있습니다. ALTER TABLESPACE 시스템 권한이 요구됩니다.

 

Renaming Datafiles in a Single Tablespace
 한 테이블스페이스의 데이터파일명을 변경할때 다음 단계를 따르도록 하십시요.

  1. 시스템 테이블스페이스를 가지고 있지 않는 데이터파일을 오프라인 시킵니다.
      ALTER TABLESPACE users OFFLINE NORMAL;
  2. OS 명령을 통해 데이터파일의 이름을 변경합니다.
  3. ALTER TABLESPACE ... RENAME DATAFILE 구문을 이용하여 오라클에게 변경된 파일 명을 새로 지정하여 줍니다.
  4. ALTER TABLESPACE .. RENAME FILE 구문을 이용해 변경하여 줍니다. 다음 예제는 /u02/oracle/rbdb1/user1.dbf 파일을 /u02/oracle/rbdb1/user01.dbf 로 변경하는 예시 입니다.

      ALTER TABLESPACE users RENAME FILE '/u01/oracle/rbdb1/user1.dbf' TO
                               /u01/oracle/rbdb1/user01.dbf';

    새로운 파일은 반드시 미리 존재해 있어야 합니다. 이 구문으로는 새로운 데이터 파일을 만들지 않습니다.
  5. 데이터베이스를 백업니다. 데이터베이스의 구조가 변경되면 항상 완전백업을 받도록 하십시요.

Relocating and Renaming Datafiles in a Single Tablespaces
 다음은 데이터파일의 위치를 변경하는 방법입니다. 우선 다음 상황을 가정합니다.

  • 운영중인 데이터베이스가 모든 데이터파일이 같은 디스크에 위치한 users 라는 테이블 스페이스를 가지고 있습니다.
  • users 테이블스페이스의 데이터파일은 각각 다른 디스크로 옮겨져야 합니다.
  • 데이터베이스에 대한 관리자 권한을 가지고 있습니다.
  • 데이터베이스를 백업해야 합니다.

 다음 절차를 따라 하세요.

  1. 관련된 데이터파일의 이름을 확인합니다.
    DBA_DATA_FILES 뷰에 질의하여 파일 이름과 크기를 확인할 수 있습니다.

    SELECT FILE_NAME, BYTES FROM DBA_DATA_FILES
    WHERE TABLESPACE_NAME='users';
  2. 해당 데이터 파일을 오프라인 시키거나 데이터베이스를 셧다운 시키고 마운트 상태로 둡니다.
  3. 해당 데이터 파일을 새로운 디스크에 복사합니다.
  4. 이제 오라클에게 해당 데이터파일이 변경되었다는 사실을 알려주어야 합니다.

    users 테이블스페이스가 가진 데이터파일의 위치정보는 콘트롤 파일에 기록되어 있습니다. 따라서 예전 이름을 새로운 위치와 이름으로 콘트롤 파일을 업데이트 하도록 합니다.

    데이터베이스가 온라인이고 테이블스페이스만 오프라인일 경우 ALTER TABLESPACE .. RENAME DATAFILE 구문을 이용합니다. 데이터베이스가 마운트 상태일 경우 ALTER DATABASE RENAME DATAFILE 구문을 사용합니다.

      ALTER TABLESPACE users RENAME DATAFILE
       '/u02/oracle/rbdb1/users01.dbf', '/u02/oracle/rbdb1/users02.dbf' TO
       '/u03/oracle/rbdb1/users01.dbf', '/u04/oracle/rbdb1/users02.dbf';
  5. 테이블스페이스를 온라인시키거나 데이터베이스를 오픈상태로 만듭니다.
    이미 데이터베이스가 오픈상태이고 테이블스페이스만 오프라인일 경우 테이블스페이스를 온라인으로 바꿉니다.
  6. 데이터베이스를 백업합니다. 데이터베이스의 구조가 변경된 후에는 반드시 완전백업을 받아두는 것이 좋습니다.

Renaming and Relocating Datafiles for Multiple Tablespaces

 ALTER DATABASE .. RENAME FILE 구문을 통해 하나 혹은 그이상의 테이블스페이스의 데이터파일명을 변경하거나 재위치 시킬 수 있습니다. 이 방법은 몇몇 테이블스페이스의 데이터파일과 위치를 한번에 변경하고자 할 때 사용할 수 있는 유일한 방법 입니다.

 여러개의 테이블스페이스의 데이터파일을 한번에 변경하거나 시스템 테이블스페이스의 데이터파일을 변경하고자 할 시 반드시 ALTER DATABASE 권한이 존재해야 합니다.

  1. 데이터베이스를 마운트 상태로 둡니다.
  2. 데이터파일을 OS 명령을 통해 원하는 위치에 원하는 이름으로 복사합니다.
  3. ALTER DATABASE RENAME FILE 구문을 이용하여 데이터파일이 변경되었음을 오라클에게 알립니다.

    예를들어 /u02/sort01.dbf 와 /u02/user3.dbf 를 /u02/temp01.dbf 와 /u02/users03.dbf 로 변경하기 위해서는 다음을 수행하면 됩니다.

      ALTER DATABASE RENAME FILE '/u02/sort01.dbf', '/u02/user3.dbf' TO
                                                      '/u02/temp.dbf', '/u02/users03.dbf';
  4. 데이터베이스를 백업합니다.

 

Dropping Datafiles

 데이터파일을 드롭하는 특정 SQL 명령문은 없습니다. 데이터파일을 드롭한다는 것은 해당 데이터파일을 소유하고 있는 테이블스페이스를 드롭한다는 의미입니다. 테이블스페이스의 데이터파일을 드롭하고 싶다면 다음 예제를 따르도록 하십시요.

  1. 새로운 테이블스페이스를 작성합니다.
  2. 예전 테이블스페이스의 데이터를 새로운 테이블스페이스로 옮깁니다.
  3. 예전 테이블스페이스를 드롭합니다.

 반면, ALTER DATABASE 구문으로 템프파일을 드롭할 수 있습니다. 다음 예제를 참고하세요.

   ALTER DATABASE TEMPFILE '/u02/oracle/data/lmtemp02.dbf' DROP INCLUE DATAFILES;

 

 

Verifying Data Blocks in Datafiles

 데이터 블록의 체크섬을 검사하고 싶다면 DB_BLOCK_CHECKSUM 초기화 파라메터를 TRUE 로 세팅하면 됩니다. 이 값은 동적으로 바꿀 수 있으며(인스턴스 재가동 불필요)  파라메터의 세팅 상태와 상관없이 시스템 테이블스페이스에 대해서는 항상 데이터 블록을 검사합니다.

 블록 검사를 수행하게 하면 오라클은 디스크에 기록된 각각의 데이터 블록에 대한 체크섬 값을 계산합니다. 체크섬은 템프파일도 포함하여 모든 데이터 블록에 적용됩니다.

 DBWn 프로세스는 각 블록에 대한 체크섬 값을 구한 후 그 정보를 블록의 헤더에 기록합니다. 체크섬은 다이렉트 로더에 의해서도 계산됩니다.

 다음번에 데이터 블록을 읽을때 블록에 문제가 있는지는 체크섬 값을 이용해 확인합니다. 문제가 발생하게 되면 ORA-01578 메세지를 반환하게 되고 문제가 된 사항을 추적 파일에 기록합니다.

 

 

Viewing Datafile Information

 다음 딕시너리 뷰들은 데이터베이스의 데이터파일에 대한 유용한 정보를 제공해 줍니다.

  • DBA_DATA_FILES
    테이블 스페이스가 가지고 있는 데이터파일에 대한 정보와 파일 번호에 대한 정보를 제공합니다. 파일 번호는 좀더 상세한 정보를 얻고자 할때 다른 뷰와 조인하여 사용할 수 있습니다.
  • DBA_EXTENTS, USER_EXTENTS
    DBA 뷰는 데이터베이스의 모든 세그먼트를 포함한 익스텐트의 정보를 보여줍니다. USER 뷰는 현 사용자가 소유한 오브젝트의 정보만 보여줍니다.
  • DBA_FREE_SPACE, USER_FREE_SPACE
    DBA 뷰는 모든 테이블스페이스의 여유 공간을 보여주며 USER 뷰는 현 사용자가 소유한 오브젝트의 정보만 보여줍니다.
  • V$DATAFILE
    콘트롤파일에 있는 데이터파일의 정보를 보여줍니다.
  • V$DATAFILE_HEADER
    데이터파일 헤더의 정보를 보여줍니다.

 

Fin.

REF) Oracle Documents (server.920/a96521)

2009년 11월 2일 월요일

ORACLE_025. Managing Job Queues

Enabling Process Used for Excuting Jobs
 잡 큐(Job Queue)를 이용하여 주기적으로 스케쥴 루틴(Jobs)을 수행할 수 있습니다. DBMS_JOB 패키지를 이용하여 작업의 스케쥴을 잡 큐에 넣을 수 있으며, 어떤 작업을 수행하게 할 지 결정할 수 있습니다. 또한 이미 적용한 작업을 변경, 보류, 혹은 삭제를 수행할 수 있습니다.

 잡 큐(Jnnn) 프로세서는 잡 큐에 있는 작업을 수행합니다. 각각의 인스턴스에서 이러한 잡 큐는 CJQ0(Coordinator Job Queue) 백그라운드 프로세스에 의해 동적으로 수행됩니다. 선택되어 수행되어질 작업들은 DBA_JOBS 뷰를 통해 확인할 수 있습니다. 시간순으로 정렬되어 있으며 선택된 작업은 Jnnn 프로세스가 선택된 작업을 수행합니다. 각각의 Jnnn 프로세스는 선택된 작업을 수행합니다.(Jnnn 프로세스는 CJQ 프로세스가 가지고 있는 하위 프로세스 입니다.)

 JOB_QUEUE_PROCESSES 초기화 파라메터로 인스턴스가 CJQ 프로세스를 시작할지 안할지를 결정할 수 있습니다. 파라메터가 0으로 선언되어 있으면 데이타베이스가 시작될때 CJQ 프로세스를 시작하지 않으며 잡 큐에 있는 작업들을 수행하지 않습니다. JOB_QUEUE_PROCESSES 파라메터는 또한 인스턴스에서 수행될 수 있는 최대 Jnnn 프로세스의 갯수를 정의하기도 합니다. 최대값은 1000 입니다.

 다음과 같이 파라메터를 설정하면 CJQ 프로세스는 데이터베이스가 시작할때 같이 시작됩니다. 그리고 최대 60개의 Jnnn 프로세스를 가지게 됩니다.

  JOB_QUEUE_PROCESS = 60

 새로운 값이 기존의 값보다 작거나 현재 수행중인 Jnnn 의 갯수보다 작으면 수행중인 프로세스는 수행중인 작업이 종료되는 순간 정지하게 됩니다.

 Jnnn 프로세스는 데이터베이스가 Restricted 상태라면 Jnnn 프로세스는 수행되지 않습니다.

 

 

 

Managing Job Queues

 

The DBMS_JOB Package

 잡 큐(Job Queue)에 있는 작업을 등록하고 관리하려면 DBMS_JOB 패키지에 있는 프로시저를 수행하면 됩니다. 작업큐를 사용하기 위해 필요한 데이터베이스 권한은 따로 존재하지 않습니다. 잡 큐를 사용할 수 있는 모든 사용자는 잡 큐 프로시저를 실행할 수 있습니다.

 다음은 DBMS_JOB 패키지에 있는 프로시저들 입니다.

  • SUBMIT :
    잡 큐에 작업을 등록합니다.
  • REMOVE :
    잡 큐에 등록되어 있는 작업을 삭제합니다.
  • CHANGE :
    잡 큐에 등록되어 있는 작업을 변경합니다. 작업의 주석을 변경하거나, 수행 시간, 수행 간격등을 조정할 수 있습니다.
  • WHAT :
    특정 작업의 주석을 변경합니다.
  • NEXT_DATE :
    특정 작업의 다음 수행 날짜를 지정합니다.
  • INTERVAL :
    특정 작업의 수행 주기를 변경합니다.
  • BROKEN :
    작업이 실패했음을 나타내는 플레그를 설정하거나 해제합니다. 실패로 설정되어 있다면 해당 작업을 수행하지 않습니다.
  • RUN :
    특정 작업을 강제로 수행합니다.

Submitting a Job to the Job Queue
 새로운 작업을 잡 큐에 넣기 위해 DBMS_JOB 패키지의 SUBMIT 프로시저를 이용합니다. SUBMIT 프로시저에서 다음 파라메터들을 사용할 수 있습니다.

  • JOB :
    출력 파라메터. 새로 생성한 작업의 식별 이름입니다. 작업을 변경하거나 삭제할때 반드시 이 번호를 사용해야 합니다.
  • WHAT :
    수행하고자 하는 PL/SQL 코드 입니다.
  • NEXT_DATE
    작업이 수행될 날짜를 정합니다. 기본값은 SYSDATE 입니다.
  • INTERVAL :
    다음번 수행될 날짜를 지정합니다. 기본값은 NULL 입니다. 반드시 시간 혹은 NULL 값만을 취합니다.
  • NO_PARSE
    플래그(FLAG)입니다. FALSE(기본값)로 설정시 해당 적업과 관련된 프로시저를 해석(PARSE)합니다. TRUE 일 경우 처음 실행될때 해석을 합니다. 예를들어 테이블을 만들기전에 작업을 SUBMIT 하기 위해선 NO_PARSE 값을 TRUE 로 놓습니다.

 예로, 다음 작업을 큐에 넣는다고 가정하고 작업 번호(jobnumber)를 출력해 보겠습니다. 작업은 DBMS_DDL.ANALYZE_OBJECT 를 호출하여 hr.employees 테이블의 통계정보를 생성합니다. 통계정보는 해당 테이블이 가지고 있는 행의 반개를 기준으로 작성됩니다. 이 작업은 매 24시간마다 수행됩니다.

  VARIABLE jobno NUMBER
  BEGIN
    DMBS_JOB.SUBMIT(:jobno,
      'DBMS_DDL.ANALYZE_OBJECT(''TABLE'',
      ''HR'',''EMPLOYEES'',
      ''ESTIMATE'',NULL, 50);',
      SYSDATE,'SYSDATE+1');
    COMMIT;
   END;
   /
   PRINT jobno

   JOBNO
   ----------
   14144

Job Environment

 작업을 잡 큐에 넣거나 혹은 변경하였을때 오라클은 다음과 같은 환경 특징을 기록합니다.

  • 현재 사용자
  • 작업을 넣거나 변경한 사용자
  • 현 스키마(ALTER SESSION SET CURRENT_SCHEMA 구문을 입력했었다면 현재 사용자와 큐에 넣은 사용자가 다를 수 있습니다.)

 또한 다음과 같은 NLS 파라메터도 함께 기록됩니다.

  • NLS_LANGUAGE
  • NLS_TERRITORY
  • NLS_CURRENCY
  • NLS_ISO_CURRENCY
  • NLS_NUMERIC_CHARATER
  • NLS_DATE_FORMAT
  • NLS_DATE_LANGUAGE
  • NLS_SORT

 오라클은 매번 작업이 수행될때 마다 이러한 환경 변수들을 복원합니다. NLS_LANGUAGE 와 NLS_TERRITORY 파라메터는 정의되지 않은 NLS 파라메터에 기본값을 제공해 줍니다.

 DBMS_SQL 패키지나 ALTER SESSION 구문을 이용하여 이러한 작업의 환경변수값을 변경할 수 있습니다.

 

Jobs and Import/Export

 작업을 익스포트 하거나 임포트 할 수 있습니다.. 따라서 해당 작업을 한 데이터베이스에서 익스포트 하여 다른 데이터베이스에 전송이 가능합니다. 익스포팅이나 임포팅 작업을 수행시, 작업 번호, 환경변수, 정의는 그대로 유지됩니다.

 

Job Owner

 작업을 잡 큐에 넣을때, 오라클은 작업을 집어넣는 사람을 작업의 소유자로 간주합니다. 작업의 소유자만이 작업을 변경하거나 강제로 수행시키고 큐에서 삭제할 수 있습니다.

 

Job Number

 큐에 들어간 작업은 작업 번호로써 구분이 됩니다. 작업을 큐에 넣을때 JOBSEQ라는 SYS소유의 시퀀스로부터 작업 번호를 자동으로 부여 받습니다. 한번 작업 번호를 할당받으면 작업번호는 변경할 수 없습니다. 작업이 익스포트 혹은 임포트 되어진다 해도 작업 번호는 불변입니다.

 

Job Definition

 작업 정의는 SUBMIT 프로시저의 WHAT 파라메터 에 정의되어 있는 PL/SQL 코드 입니다. 보통, 작업 정의는 단일 프로시저 호출로 정의됩니다. 프로시저는 여러개의 파라메터를 호출 할 수 있습니다.

 다음 예시는 올바른 작업 정의 입니다.

  • 'myproc(''10-JAN-99'', next_date, broken);'
  • 'scott.emppackage.give_raise(''JFEE'',3000.00);'
  • 'dbms_job.remove(job);'

Job Execution Interval

 작업이 일정한 간격으로 주기적으로 실행되어야 한다면 INTERVAL 파라메터에 'SYSDATE + 7'과 같은 날짜 표현식을 사용하시면 됩니다. 다음 예시를 참고하세요.

  • 'SYSDATE + 7'  :
    마지막 수행 후 정확히 7일 후
  • 'SYSDATE + 1/48' :
    매 30분 마다
  • 'NEXT_DAY(TRUNC(SYSDATE),''MONDAY'') + 15/24'
    매 월요일 오후 3시마다
  • 'NEXT_DAY(ADD_MONTH(TRUNC(SYSDATE),''Q''),3),''THURSDAY'')'
    매 분기의 첫번째 목요일

 항상 특정 시간에 작업을 수행하게 만들고 싶으면 INTERVAL 과 NEXT_DATE 파라메터를 'NEXT_DAY(TRUNC(SYSDATE),''MONDAY'');' 와 비슷한 방식으로 선언해 주시면 됩니다.

 

Database Links and Jobs

 데이터베이스 링크를 사용하는 작업을 큐에 넣으면 해당 링크는 반드시 사용자 이름과 암호를 포함시켜야 합니다. 익명의 데이터베이스 링크는 작업을 성공적으로 수행할 수 없습니다.

 

 

How Job Execution

 Jnnn 프로세스가 작업을 수행합니다. 작업을 수행하기 위해 프로세스는 세션을 생성합니다. Jnnn프로세스가 작업을 수행할때 작업의 소유자와 같은 권한을 가진 환경에서 수행됩니다. 작업에 기술되어 있는 오브젝트들에 접근하기 위한 권한이 모두 있는지 반드시 확인하십시요.

 DBMS.JOB.RUN 프로시저를 이용해 강제로 작업을 수행할때 작업은 사용자 프로세스(User process)와 기본 권한을 가진 상태로만 수행합니다. 롤을 통해 위임받은 권한들은 사용할 수 없습니다. 작업이 수행되는 중 참조하게 되는 오브젝트들에 대한 접근 권한이 있는지 확인하십시요.

 

Job Queue Locks

 한번에 한 세션에서만 작업이 수행될 수 있도록 오라클은 수행중인 작업에 대해 잡 큐를 잠급니다. 작업이 수행될때 해당 세션은 해당 작업에 대해 잠금(Lock)을 겁니다. 세션이 현재 잡고 있는 잠금에 대한 정보는 데이터 딕셔너리를 통해 확인해 볼 수 있습니다.

 다음 쿼리를 참고하세요.


  SELECT SID, TYPE, ID1, ID2 FROM V$LOCK WHERE TYPE = 'JQ';
            SID   TY        ID1       ID2
             12    JQ          0    14114
   1 row selected.

 위 쿼리에서 잠금(Lock)을 잡고 있는 세션은 12 입니다. ID1 열은 JQ 블럭에 대해 항상 0 입니다. ID2 열은 세션이 수행중인 작업의 작업 번호 입니다. 위 뷰는 DBA_JOBS_RUNNING 뷰와 결합(JOIN) 하여 좀더 상세한 정보를 획득할 수 있습니다.

 

Job Execution Errors

 작업이 실패했을시, 추적파일(trace file)과 alert log 파일에 해당 정보가 기록됩니다. ORA-12012 메세지를 작성하고 실패한 작업의 작업 번호를 기록합니다.

 다음의 경우 잡 큐에 있는 작업이 실패합니다.

  • 네트워크나 인스턴스의 실패
  • 작업 수행중 발생한 예외

 작업을 수행하기 위해 시도하는 중 에러가 반환되면 오라클은 다시한번 그 작업을 수행하려 합니다. 첫번째 재시도는 실패후 1분후, 두번째 시도는 2분후, 세번째 시도는 4분후... 이런 방식으로 전 시도의 대기 시간보다 2배의 시간 간격을 두고 재시도 합니다. 작업이 16번 실패하면 오라클은 자동으로 작업에 BROKEN 플레그를 선언하고 더이상 그 작업을 수행하지 않습니다. 하지만 재시도 중간 중간 작업이 제대로 돌아갈 수 있게 문제를 해결할 수 있는 기회들이 있습니다. 이 기회들은 오라클이 작업을 재수행 하는데 방해를 주지 않습니다.

 

 

Removing a Job from the Job Queue
 잡 큐에서 작업을 삭제하기 위해 REMOVE 파라메터를 사용할 수 있습니다.
 다음 구문은 잡 큐에서 14114의 작업번호를 가진 작업을 잡큐에서 삭제하는 방법입니다.

  BEGIN
   DBMS_JOB.REMOVE(14114);
  END;
  /

 제한사항

  • 현재 수행중인 작업을 잡 큐에서 제거할 수 있습니다만, 수행중인 작업은 중단되지 않고 끝까지 수행됩니다.
  • 자기 자신의 작업만 삭제할 수 있습니다. 자신의 소유 작업이 아닌 작업을 삭제하려 할 경우 해당 작업이 잡큐에 존재하지 않는다는 메세지를 받게 될 것 입니다.

 

Altering a Job

 잡 큐에 있는 작업을 변경하기 위해 DBMS_JOB 패키지의 CHANGE, WHAT, NEXT_DATE 혹은 INTERVAL 프로시저를 사용할 수 있습니다. 오직 자신이 소유하고 있는 작업만 변경할 수 있습니다.

 

CHANGE
 DBMS_JOB.CHANGE 프로시저를 이용하여 사용자가 정의한 어떠한 파라메터도 수정할 수 있습니다. 다음 예제를 참고하세요.

  BEGIN
  DBMS_JOB.CHANGE(14114, NULL, NULL, 'SYSDATE +3');
  END;
  /

 WHAT, NEXT_DATE 혹은 INTERVAL 을 NULL 값으로 정하면 기존에 있는 값이 변동되지 않고 그대로 유지되게 됩니다.

 

WHAT
 DBMS_JOB.WHAT 프로시저를 이용하여 작업을 변경할 수 있습니다.

  BEGIN
  DBMS_JOB.WHAT(14114,
              'DBMS_DDL.ANALYZE_OBJECT(''TABLE'',''HR'',''DEPARTMENTS'',''ESTIMATE'',
              NULL,50);');
  END;
  /

 

NEXT_DATE
 DBMS_JOB.NEXT_DATE 프로시저를 이용하여 다음 수행될 시간을 변경할 수 있습니다.

  BEGIN
  DBMS_JOB.NEXT_DATE(14114, SYSDATE + 4);
  END;
  /

 

INTERVAL
 DBMS_JOB.INTERVAL 프로시저를 이용하여 작업과 작업 사이의 간격을 결정할 수 있습니다.

  BEGIN
  DBMS_JOB.INTERVAL(14114,'NULL');
  END;
  /

 위 예시의 경우 작업은 더이상 수행되지 않고 잡 큐에서 제거될 것 입니다.

 

 

Broken Jobs

 오라클은 BROKEN 플래그가 있는 작업은 수행하지 않습니다. 하지만 DBMS_JOB.RUN. 프로시저를 이용하여 강제로 작업을 수행할 수 있습니다.

 

How a Job Becomes Broken

 실패하지 않을거라 생각되는 작업을 잡 큐에 넣습니다. 그럼에도 BROKEN 이 발생하는 경우는 다음과 같습니다.

  • 16번의 재시도가 모두 실패할 경우
  • DBMS_JOB.BROKEN 을 이용하여 작업에 BROKEN 플레그를 설정한 경우
    BEGIN
    DBMS_JOB.BROKEN(14114);
    END;
    /

 오라클은 한번 BROKEN 마크를 단 작업은 사용자가 BROKEN 마크를 해제하기 전까지나 DBMS_JOB.RUN 프로시저를 호출하지 않는한 다시는 수행하지 않습니다.

 다음 예시는 BROKEN 으로 표시된 14114 작업을 다음 월요일 에 수행될 수 있도록 하는 방법입니다.

  BEGIN
  DBMS_JOB.BROKEN(14114, FALSE, NEXT_DAY(SYSDATE, 'MONDAY'));
  END;
  /

Running Broken Jobs

 16번의 작업 실패가 일어났다면 작업은 BROKEN 으로 표시됩니다. 문제를 해결했다면 다음 방법을 통해 작업을 재개할 수 있습니다.

  • DBMS_JOB.RUN 을 통한 강제 수행
  • DBMS_JOB.BROKEN 을 통한 BROKEN 플레크 해제

 DBMS_JOB.RUN 프로시저를 통해 강제로 작업을 수행하면 오라클은 해당 작업을 즉시 수행합니다. 작업이 성공적으로 수행되면 BROKEN 플레그는 해제되며 실패 횟수가 초기화 됩니다.

 

 

Forcing a Job to Execute

 작업을 스케쥴에 맞춰 수행하는 것이 아닌 직접 수행해 주어야 할 때가 있을 수도 있습니다. 실패한 작업을 수정을 했을경우 수정한 작업을 바로 수행시켜 보고 싶을 것 입니다. 작업을 강제로 수행하기 위해서는 DBMS_JOB 패키지의 RUN 프로시저를 사용합니다.

 DBMS_JOB.RUN 프로시저를 수행할때 다음번 작업이 시작할 시간을 다시 계산 합니다. 예를들어 월요일에 작업을 새로 작성하면서 NEXT_DATE 값으로 SYSDATE를 주고, INTERVAL 에 'SYSDATE +7' 이라고 정해주었다면 해당 작업은 매 월요일에 수행 될 것 입니다. 하지만 수요일날 RUN 프로시저를 사용하여 강제로 작업을 수행했다면 다음 작업이 수행되는 날은 다음주 수요일이 될 것 입니다.

 다음 구문은 14114 작업을 현 세션에서 수행하고 다음 수행날자를 재 계산하여 입력되는 예제 입니다.

  BEGIN
  DBMS_JOB.RUN(14114);
  END;

 

Terminating a Job
 수행중인 작업을 중지시키는 방법으로 해당 작업에 BROKEN 플레그를 설정하고 해당 작업이 수행되는 세션을 확인한 다음 그 세션을 잘라내는 방법을 사용합니다. BROKEN 설정을 하는 이뉴는 오라클이 해당 작업을 다시 수행하지 않게 하기 위함 입니다.

 

 작업을 수행하는 세션을 확인후에 (V$SESSION 혹은 V$LOCK 뷰를 이용합니다.) SQL 구문인 ALTER SYSTEM 으로 해당 세션을 잘라낼 수 있습니다.

 

 

Viewing Job Queue Information
 다음에 나열된 딕셔너리 뷰를 통하여 잡 큐와 작업에 대해 확인할 수 있습니다.

  • DBA_JOBS, ALL_JOBS, USER_JOBS
    DBA 뷰는 데이터베이스 내의 모든 작업에 대하여 보여줍니다. ALL 은 현 사용자가 접근할 수 있는 모든 작업에 대한 리스트를 반환하며 USER 뷰는 현 사용자가 소유하고 있는 작업에 대해서만 보여줍니다.
  • DBA_JOBS_RUNNING
    현 데이터베이스가 수행중인 작업의 목록을 보여줍니다. 이 뷰는 V$LOCK 뷰와 조인하여 잠금 (LOCK)을 가진 작업을 확인할 수 있습니다.

Displaying Information About a Job
 다음 쿼리는 작업 번호, 다음 수행일, 실패 횟수, BROKEN 상태를 보여줍니다.

  SELECT JOB, NEXT_DATE, NEXT_SEC, FAILURES, BROKEN
  FROM DBA_JOBS;

Displaying Information About Running Jobs
 현재 수행중인 작업만 조회하여 볼 수도 있습니다. 다음 쿼리는 현제 데이터베이스가 수행중인 작업에 대하여 세션 식별자(Session Identifier -SID), 작업 번호, 작업을 등록한 사람, 그리고 시작 시간을 보여줍니다.

  SELECT SID, r.JOB, LOG_USER, r.THIS_DATE, r.THIS_SEC
  FROM DBA_JOBS_RUNNING r, DBA_JOBS j
  WHERE r.JOB = j.JOB;

 

Fin.
REF) Oracle documents (server.920/a96521)