오라클 테이블스페이스 자동 관리 프로시저

테이블 스페이스 용량 관리는 DBA 의 업무중 하나로,
지속적으로 모니터링 하고 용량을 늘려주는 작업을 해야한다.

DBA 의 운영 업무 중 가장 큰 스트레스 중 하나는 테이블 스페이스의 용량이 부족하여 DB에 심각한 장애가 발생하는 경우이다. 

특히, 개발 단계에서 대량 배치 작업이나 데이터 이관 작업을 심야 시간에 진행 시 dba가 항상 상주하여 모니터링 할 수 없는 경우가 발생한다. 이러한 상황에 대비하여 DBA 가 부재한 상황에서 자동으로 테이블 스페이스를 모니터링하고 테이블스페이스의 데이터파일 평균 사용률이 임계치에 달하면 테이블 스페이스를 증설하는 프로시저를 소개하고자 한다. 


1. 프로시저 개요

  • 기능:
    • 테이블스페이스의 데이터파일 평균 사용률이 임계치를 초과한 테이블스페이스에 데이터파일 자동 추가
  • 주요 사용 매개변수:
    • p_threshold_percent: 테이블스페이스의 데이터파일 평균 사용률 임계치 (%) 설정
    • p_datafile_add_size: 추가 데이터파일 크기 지정 (예: 1M, 1G 등)
  • 프로시저 흐름도

2. 단계별 프로세스 설명

1. SCHEDULE JOB 설정

  • 수행 작업:
    • 5분 주기로 테이블스페이스의 데이터파일 평균 사용률 임계치 80% 추가할 데이터파일의 크기가 1M 인 테이블스페이스 자동 증가 프로시저 실행
BEGIN
DBMS_SCHEDULER.CREATE_JOB
    ( JOB_NAME => 'DBADM.JOB_TS_DATAFILE_AUTO_ADD'
        , START_DATE => SYSDATE
        , REPEAT_INTERVAL => 'FREQ=MINUTELY; INTERVAL=5' -- 수행주기
        , JOB_TYPE => 'PLSQL_BLOCK'
        , JOB_ACTION => 'BEGIN SP_TS_DATAFILE_AUTO_ADD(80,''1M''); END;'
        );
DBMS_SCHEDULER.ENABLE
    (NAME => 'DBADM.JOB_TS_DATAFILE_AUTO_ADD');
END;
/

현재 진행중인 프로젝트의 개발/테스트 환경에서는 5분 단위로 테이블 스페이스를 모니터링하여 임계치가 80%, 데이터파일 사이즈를 1M 단위로 추가하고 있다. 

2. AUTOEXTEND 속성 설정

  • 조건:
    • 데이터파일이 AUTOEXTEND OFF로 설정되어 있거나, AUTOEXTEND ON이지만 MAXSIZE가 제한된 경우.
  • 수행 작업:
    • AUTOEXTEND를 활성화하고 MAXSIZE를 UNLIMITED로 설정.
ALTER DATABASE DATAFILE 'file_name' AUTOEXTEND ON MAXSIZE UNLIMITED;
  • 실행 결과:
    • 데이터파일이 자동 확장이 가능하도록 설정되어, 공간 부족 문제를 예방.

3. 테이블스페이스 사용률 모니터링

  • 사용률 계산 로직:
    • AUTOEXTEND = YES: (사용된 용량 / 최대 크기) * 100
    • AUTOEXTEND = NO: (사용된 용량 / 현재 크기) * 100
  • 조건:
    • 입력받은 임계치(p_threshold_percent)를 기준으로 테이블스페이스의 데이터파일 평균 사용률을 계산
    • AUTOEXTENSIBLE이 활성화된 경우와 비활성화된 경우 각각 다른 방식으로 사용률을 계산
  • 실행 결과:
    • 임계치 초과 여부를 판단.
SELECT TABLESPACE_NAME, AVG(USAGE_PERCENT)
  FROM (사용률 계산 로직)
WHERE USAGE_PERCENT > p_threshold_percent;

4. 데이터파일 자동 추가

  • 조건:
    • 테이블스페이스의 데이터파일 평균 사용률이 임계치를 초과한 경우 지정된 크기(p_datafile_add_size)의 데이터파일을 추가
  • 수행 작업:
    • 새로운 데이터파일 추가:
      • 데이터파일 경로: +DATA
      • 크기: p_datafile_add_size
      • AUTOEXTEND ON, MAXSIZE UNLIMITED 설정.
ALTER TABLESPACE tablespace_name
ADD DATAFILE '+DATA' SIZE p_datafile_add_size AUTOEXTEND ON MAXSIZE UNLIMITED;
  • 실행 결과:
    • 테이블스페이스 공간이 자동 확장됨.

5. 작업 내용 기록

  • DDL 실행 내역 기록
    • 모든 DDL 변경 사항(ALTER DATABASE DATAFILE, ALTER TABLESPACE)을 DBADM.TB_DDL_HISTORY 테이블에 저장

6. 예외 처리 및 재확인 로직

  • 예외 처리:
    • NO_DATA_FOUND 예외 발생 시 프로시저 종료.
  • 재확인 로직:
    • 테이블스페이스의 데이터파일 평균 사용률 재확인 후 필요 시 데이터파일 추가를 재실행.

3. 예상 활용 시나리오

아래 시나리오를 통해 부족한 공간이 DBA의 개입 없이 자동으로 확장되는지 확인

CASE 1 : AUTOEXTEND OFF, TABLESPACE SIZE 10M 인 테이블 스페이스에 10M 이상인 테이블 생성

생성된 테이블스페이스와 데이터 파일 확인

ALERT.LOG 확인 – AUTOEXTEND ON MAXSIZE UNLIMITED 로 ALTERE 됨.

CASE 2 : 테이블스페이스의 데이터파일 평균 사용률이 임계치(80%) 를 초과한 테이블스페이스 데이터 파일 자동 추가

 AUTOEXTEND 허용된 TABLESPACE에 28G 테이블 생성 (임계치 80% 이상)

AUTOEXTEND 활성화된 상태로 자동 확장되는 것을 볼 수 있다.

테이블 생성 완료 후  테이블스페이스의 데이터파일 평균 사용률 조회. 83% 사용중으로 확인

ALERT.LOG 조회 – TABLESPACE 에 자동으로 데이터파일이 추가됨.

테이블 스페이스의 데이터 파일 조회 시 새롭게 생성된 데이터파일을 확인할 수 있음.

로그 테이블 조회로 수행된 작업 다시 확인.


4. 결론 및 개선 방향

  • 결론
    • 테이블스페이스 자동관리 프로세스는 DBA 작업을 보조해주어 업무를 자동화하고 장애를 예방하는데 도움이 되지만 작업에 따라서 (전환, 배치) 임계치를 낮추고, 데이터 파일 사이즈를 크기(30G) 키우는 등 DBA 판단하에 변수를 수정하고 많은 테스트 후 사용해야한다. 테이블스페이스를 크게 증설하는 작업의 경우 데이터 파일을 추가하는 도중 공간이 차 장애가 날 수 있기 때문에 임계치를 더 낮게 잡는것을 권장한다. 
  • 오류 처리 강화 및 병렬 처리
    • 데이터파일 추가 혹은 변경 실패 시, 보다 상세한 오류 로그 필요
    • 대규모 테이블스페이스 및 데이터파일 환경에서 성능 향상을 위해 병렬 처리 고려해볼 필요가 있음
    • 병렬 작업으로 대량 데이터 이관 시 스케줄링 된 시간보다 공간의 확장이 더 크다면 에러가 발생할 수 있음.작업 전 예상 용량을 산정해 미리 늘려 두는 것이 좋다.

※주의※ 운영 환경에서 사용하기 위해서는 위험요소가 존재할 수 있으므로 많은 테스트 후 적용이 필요하다. (개발 및 테스트 단계에서 사용하기를 권장)

※ 작업에 따라 임계치와 추가할 데이터파일 사이즈를 조정하여 사용하는 것을 권고한다.


[참고] 프로시저, 로그테이블

CREATE OR REPLACE PROCEDURE DBADM.SP_TS_DATAFILE_AUTO_ADD (
    p_threshold_percent IN NUMBER,      -- 테이블스페이스 사용률 모니터링 임계치 (%)
    p_datafile_add_size IN VARCHAR2     -- 추가할 데이터파일 사이즈 (예: 1M, 1G)
) AUTHID CURRENT_USER IS
   
    v_tablespace_name dba_data_files.tablespace_name%TYPE; -- 테이블스페이스명
    v_file_name       dba_data_files.file_name%TYPE;       -- 파일명
    v_count           NUMBER;
    v_ddl_1           VARCHAR2(4000);
    v_ddl_2           VARCHAR2(4000);
   
BEGIN
    -- AUTOEXTEND 미적용 OR 적용되었으나 MAXSIZE가 UNLIMITED로 설정되지 않은 데이터파일 옵션 변경
    FOR rec_df IN (
        SELECT TABLESPACE_NAME,
              FILE_NAME,
              AUTOEXTENSIBLE,
              MAXBYTES
          FROM DBA_DATA_FILES
        WHERE TABLESPACE_NAME LIKE 'TS$_%' ESCAPE '$'
          AND (AUTOEXTENSIBLE = 'NO' OR (AUTOEXTENSIBLE = 'YES' AND MAXBYTES < 34359721984))
    ) LOOP
        v_file_name := rec_df.file_name;
        v_ddl_1 := 'ALTER DATABASE DATAFILE ''' || v_file_name || ''' AUTOEXTEND ON MAXSIZE UNLIMITED';
       
        EXECUTE IMMEDIATE v_ddl_1;
        DBMS_OUTPUT.PUT_LINE('[1. Alter Datafile] : ' || v_ddl_1);
        -- 수행 로그 INSERT
        INSERT INTO DBADM.TB_DDL_HISTORY
            SELECT SYSDATE,
                  ORA_LOGIN_USER,
                  'ALTER',
                  'DATABASE DATAFILE',
                  ORA_DICT_OBJ_OWNER,
                  v_file_name,
                  PROGRAM,
                  MACHINE,
                  SYS_CONTEXT('USERENV', 'IP_ADDRESS'),
                  OSUSER,
                  SQL_ID,
                  v_ddl_1
              FROM V$SESSION
            WHERE SID = SYS_CONTEXT('USERENV', 'SID');
           
        COMMIT;
    END LOOP;

    <<label_add_datafile>>
    -- 테이블스페이스 사용률 모니터링 및 임계치 초과 시 데이터파일 추가
    FOR rec_ts IN (
        SELECT X.TABLESPACE_NAME,
              AVG(X.USAGE_PERCENT) AS USAGE_PERCENT
          FROM (
                SELECT DF.TABLESPACE_NAME,
                      CASE
                          WHEN DF.AUTOEXTENSIBLE = 'YES'
                          THEN ROUND((DF.BYTES - NVL(F.FREE_BYTES, 0)) / DF.MAXBYTES * 100, 2)
                          ELSE ROUND((DF.BYTES - NVL(F.FREE_BYTES, 0)) / DF.BYTES * 100, 2)
                      END AS USAGE_PERCENT
                  FROM DBA_DATA_FILES DF,
                      (SELECT FILE_ID, SUM(BYTES) AS FREE_BYTES
                          FROM DBA_FREE_SPACE
                        GROUP BY FILE_ID) F
                WHERE DF.FILE_ID = F.FILE_ID(+)
                  AND DF.TABLESPACE_NAME LIKE 'TS$_%' ESCAPE '$'
              ) X
        GROUP BY X.TABLESPACE_NAME
        HAVING AVG(X.USAGE_PERCENT) > p_threshold_percent
    ) LOOP
        v_tablespace_name := rec_ts.tablespace_name;
        v_ddl_2 := 'ALTER TABLESPACE ' || v_tablespace_name ||
                  ' ADD DATAFILE ''+DATA'' SIZE ' || p_datafile_add_size ||
                  ' AUTOEXTEND ON MAXSIZE UNLIMITED';
       
        EXECUTE IMMEDIATE v_ddl_2;
        DBMS_OUTPUT.PUT_LINE('[2. Add Datafile] : ' || v_ddl_2);
       
        INSERT INTO DBADM.TB_DDL_HISTORY
            SELECT SYSDATE,
                  ORA_LOGIN_USER,
                  'ALTER',
                  'DATABASE DATAFILE',
                  ORA_DICT_OBJ_OWNER,
                  v_file_name,
                  PROGRAM,
                  MACHINE,
                  SYS_CONTEXT('USERENV', 'IP_ADDRESS'),
                  OSUSER,
                  SQL_ID,
                  v_ddl_2
              FROM V$SESSION
            WHERE SID = SYS_CONTEXT('USERENV', 'SID');
           
        COMMIT;
    END LOOP;

    -- 임계치를 초과하는 테이블 스페이스가 있는지 확인 후 있으면 라벨 <<label_add_datafile>> 수행
    BEGIN
        SELECT COUNT(*) INTO v_count
          FROM (
                SELECT DF.TABLESPACE_NAME,
                      CASE
                          WHEN DF.AUTOEXTENSIBLE = 'YES'
                          THEN ROUND((DF.BYTES - NVL(F.FREE_BYTES, 0)) / DF.MAXBYTES * 100, 2)
                          ELSE ROUND((DF.BYTES - NVL(F.FREE_BYTES, 0)) / DF.BYTES * 100, 2)
                      END AS USAGE_PERCENT
                  FROM DBA_DATA_FILES DF,
                      (SELECT FILE_ID, SUM(BYTES) AS FREE_BYTES
                          FROM DBA_FREE_SPACE
                        GROUP BY FILE_ID) F
                WHERE DF.FILE_ID = F.FILE_ID(+)
                  AND DF.TABLESPACE_NAME LIKE 'TS$_%' ESCAPE '$'
              ) X
        GROUP BY X.TABLESPACE_NAME
        HAVING AVG(X.USAGE_PERCENT) > p_threshold_percent;
       
        IF v_count >= 1 THEN
            GOTO label_add_datafile;
        END IF;
       
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            RETURN;
    END;
   
END;
CREATE TABLE DBADM.TB_DDL_HISTORY
(
  EXEC_DTM DATE, -- 실행시간
  USERNAME VARCHAR2(128 BYTE), -- 접속계정
  ACTION_EVENT VARCHAR2(100 BYTE), -- 수행한작업 EX) CREATE, DROP
  OBJECT_TYPE VARCHAR2(32  BYTE),
  OBJECT_OWNER VARCHAR2(128 BYTE),
  PROGRAM VARCHAR2(48 BYTE),
  MACHINE VARCHAR2(64  BYTE), -- 접속기기
  IP_ADDRESS VARCHAR2(45  BYTE), -- 접속 IP
  OSUSER VARCHAR2(30  BYTE), -- OS유저명
  SQL_ID VARCHAR2(13  BYTE),
  SQL_TEXT VARCHAR2(4000 BYTE), -- 수행한 쿼리
)
TABLESPACE TS_DBADM_DN01;

CREATE INDEX DBADM.IX_DDL_HISTORY_01 ON DBADM.TB_DDL_HISTORY(OBJECT_NAME);
CREATE INDEX DBADM.IX_DDL_HISTORY_02 ON DBADM.TB_DDL_HISTORY(OBJECT)OWNER);
CREATE INDEX DBADM.IX_DDL_HISTORY_03 ON DBADM.TB_DDL_HISTORY("EXEC_DTM" DESC);