블로그 이미지
redkite

카테고리

분류 전체보기 (291)
00.SI프로젝트 산출물 (0)
00.센터 운영 문서 (0)
01.DBMS ============.. (0)
01.오라클 (117)
001.DB 관리 (19)
002.DB 마이그레이션 (8)
003.DB 백업 및 복구 (20)
004.DB 보안 (8)
005.DB 설치 (7)
006.DB 스크립트 (0)
007.DB Knowledge Bas.. (38)
008.DB 통계 및 공간 관리 (3)
009.DB Trouble Shoot.. (14)
010.교육자료 (0)
999.테스트 (0)
01.MS-SQL (15)
01.MySQL (30)
01.PostgreSql (0)
01.DB튜닝 (28)
====================.. (0)
02.SERVER ==========.. (0)
02.서버-공통 (11)
02.서버-Linux (58)
02.서버-Unix (12)
02.서버-Windows (2)
====================.. (0)
03.APPLICATION =====.. (11)
====================.. (0)
04.ETC =============.. (0)
04.보안 (5)
====================.. (0)
05.개인자료 (1)
06.캠핑관련 (0)
07.OA관련 (1)
Total
Today
Yesterday

달력

« » 2024.12
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

공지사항

최근에 올라온 글

'01.오라클/007.DB Knowledge Base'에 해당되는 글 38건

  1. 2013.02.20 [오라클]DB링크 개념
  2. 2013.02.20 [오라클]비용기반 옵티마이저
  3. 2013.02.20 [오라클]DELETE와 TRUNCATE의 차이점
  4. 2013.02.01 [오라클]NLS_LANG 관련 정보
  5. 2013.01.23 [오라클] V$ object 와 object 의 차이점
  6. 2012.12.19 [오라클]전문가가 되기 위해 무엇을 준비해야 하는가?
  7. 2012.12.19 [오라클]데이터베이스 설계의 핵심 개념을 잡아라
  8. 2012.12.19 [오라클]전문가가 되고자 하는 사람들을 위하여
  9. 2012.12.19 [오라클]통제 관리 능력을 어떻게 키울 것인가
  10. 2012.12.19 [오라클]백업-복구 가이드 - 논리적/물리적 구조의 이해
  11. 2012.12.19 [오라클]NLS의 찰떡궁합 들여다보기 2
  12. 2012.12.19 [오라클]NLS의 찰떡궁합 들여다보기 1
  13. 2012.12.19 [오라클]유지 관리 계획
  14. 2012.12.19 [DB튜닝]개발자를 위한 튜닝 가이드 - 쿼리 디자인
  15. 2012.12.19 [오라클]성능 향상을 위한 파티션 테이블 사용은 필수!!!
  16. 2012.12.19 [오라클]Create Table As Select(CTAS)
  17. 2012.12.19 [오라클]파티션 인덱스 상태 변경
  18. 2012.12.19 [오라클]파티션 로컬 인덱스 생성방법
  19. 2012.12.19 [오라클]Delete와 Truncate 비교
  20. 2012.12.19 [오라클]V$session Command 컬럼의 코드표
  21. 2012.12.19 [오라클]exp/imp 단순 이관 방법 및 순서
  22. 2012.12.19 [오라클]Data Type - DECODE 함수와 ORA-01722
  23. 2012.12.19 [오라클]PK컬럼 순서와 데이터베이스 성능
  24. 2012.12.19 [오라클]설치 시 커널 매개변수 설정
  25. 2012.12.19 [오라클]INDEX 생성 가이드라인
  26. 2012.12.19 [오라클] Oracle 8i Nosegment Index vs. Oracle 11g Invisible Index
  27. 2012.12.19 [오라클]데이터 베이스 아키텍쳐
  28. 2012.12.19 [오라클]데이터 파일 최대 지원 수
  29. 2012.12.19 [오라클]JDBC와 JDK 대응 버젼
  30. 2012.12.19 [오라클]RAC 기본 명령어

한 데이터베이스에서 네트워크상의 다른 데이터베이스에 접속하기 위한 설정을 해주는 오라클 객체 입니다. 
DB Link 설정을 하면 한 DB에서 다른 DB의 내용을 볼 수 있게 되는 것이죠. 

쉽게 말하자면 'A' DB에서 'B' DB로 DB Link 를 걸고자 한다면 우선 'A' DB의 TNSNAMES.ORA파일에 'B' DB 접속정보를 추가해 줍니다. 

물론 그 전에 두 DB 간에 1521(기본 포트)가 열려 있어야 합니다. 
접속이 가능한지는 한 DB에서 다른 DB로 > telnet IP PORT 로 접속 테스트를 해주시면 됩니다. 
ex> telnet 192.168.0.1 1521 

실제 DB Link를 걸기 위해서는 'B' DB의 서비스 네임과 link걸 table의 user/passwd를 알아야 합니다. 
그리고 'A' DB에서 DB Link를 걸기 위해선 권한이 필요합니다. 

'A' DB 의 System 계정으로 들어가서 

 GRANT CREATE PUBLIC DATABASE LINK, DROP PUBLIC DATABASE LINK TO A DB_ID; 

 'A' DB에 권한을 주고, 아래와 같이 DB Link를 생성합니다. 

CREATE DATABASE LINK TEST_LINK CONNECT TO B DB_ID IDENTIFIED BY PASSWORD USING 'B DB' 

      TEST_LINK  -> Link 이름 
      B DB_ID -> B DB 접속 아이디 
      PASSWORD  -> B DB 접속 패스워드 
    'B DB'  -> B DB 의 TNSNAMES.ORA에 등록된 Name 

  
마지막으로 아래의 명령어로 DB Link 가 걸렸는지 확인해 줍니다. 

SELECT * FROM Table@TEST_LINK 

  

* 모든 DB Link 를 확인하는 SQL 
 select * from all_db_links; 

* DB Link 삭제 
drop database link TEST_LINK 

  

  

CREATE  DATABASE LINK 원하는 링크이름 

 CONNECT TO 계정 IDENTIFIED BY 비번 
 USING '연결할 DB의 TNS'; 


이래저래 안걸리면 직접넣기 

CREATE DATABASE LINK 링크이름 
 CONNECT TO 아이디 IDENTIFIED BY 비번 
 USING '(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=호스트명)(PORT=1521)) (CONNECT_DATA=(SERVICE_NAME=SID)))';  

Posted by redkite
, |
============================================================================== 
옵티마이저 종류 

 - 공식기반 옵티마이저 : 오라클 6.x대 버젼까지 쓰던 옵티마이저로써, 공식에 의해 
                                실행. 
 - 비용기반 옵티마이저 : 오라클 7.x대 버젼부터 ~ 현재까지 쓰는 것으로써, 유리한 
    처리방식을 선택하여, 실행 
                                테이블의 데이터크기, 상황에 따라서 처리방식이 달라진다. 
=============================================================================== 

비용기반 옵티마이저 환경설정 

    - 환경설정 : 기본설정은 공식기반하고 같다. 하지만 중요한것은 룰베이스는 공식에의해 
  적절하게 작성하면 되지만, cost베이스는 반드시해야할 것이 있다. 
  ANALYZE TABLE에 의해 해당되는 테이블에 대한 통계정보를 반드시 만들어야 
                한다. 상황에 따라 처리방식이 달라지게 때문에 그러한 이유때문에 그때마다 
                통계를 작성해야한다. 밑의 설정의 3번. 

    1) SESSION-LEVEL : ALTER SESSION SET OPTIMIZER_MODE = CHOOSE; 
    2) INSTANCE-LEVEL : OPTIMIZER_MODE = CHOOSE 
    3) ANALYZE TABLE emp [ COMPUTE STATISTICS ]; 
    [ ESTIMATE STATISTICS n [ percent | rows ]; 
    [ DELETE STATISTICS ]; 
 1번이나 2번중 아무거나 설정해도 상관없음. 

============================================================================== 

실행계획 

    : 분석하고자 하는 테이블에 index도 있다면 index도 반드시 통계를 작성해야한다. 

 CREATE INDEX I_emp_deptno ON big_emp(deptno); 
 CREATE unique INDEX I_emp_empno ON big_emp(empno); 

 ANALYZE TABLE big_emp COMPUTE STATISTICS; 
 ANALYZE INDEX I_emp_deptno COMPUTE STATISTICS; 
 ANALYZE INDEX I_emp_empno COMPUTE STATISTICS; 

 SELECT ename 
 FROM big_emp 
 WHERE deptno = 20 
 AND empno BETWEEN 100 AND 200 
 ORDER BY ename; 

 Execution Plan 
 ------------------------------------------------------------------------ 
 0 SELECT STATEMENT Optimizer=CHOOSE 
 1 0 SORT (ORDER BY) 
 2 1 TABLE ACCESS (BY INDEX ROWID) OF 'BIG_EMP' 
 3 2 INDEX (RANGE SCAN) OF 'I_EMP_EMPNO' (NON-UNIQUE) 
    실행통계를 보면 Rule base에서 deptno = 20가 우선순위면에서 높아서 deptno를 기준으로 
    조회를 하는 반면 Cost base에선 데이터양에 따라 AND empno BETWEEN 100 AND 200 적게 조회 
    되어 INDEX (RANGE SCAN) OF 'I_EMP_EMPNO' (NON-UNIQUE) scan을 하게 된다. 
============================================================================== 

1) Full-Table Scan 

    Cost = 전체 블록수 / DB_FILE_MULTIBLOCK_READ_COUNT 

 (1) 전체 블록수는 ANALYZE TABLE 명령어 수행 후 USER_TABLES의 BLOCKS 컬럼의 값 
 (2) DB_FILE_MULTIBLOCK_READ_COUNT는 INIT<DB명>.ora 파일에 정의(기본값 : 8) 

    Full-Table Scan의 경우 한 블럭당 검색을 하려면 너무 늦기 때문에 .ora파일에 설정되어 
    있는 기본값 8개씩 검색을 한다. 

    ANALYZE TABLE big_emp COMPUTE STATISTICS; 
 SELECT blocks FROM user_tables 
 WHERE table_name = ‘BIG_EMP’; 
 Blocks                                        370 
------------- COST = ------------ = 46 
 370                                                8 
============================================================================== 

2) INDEX SCAN 
    Cost = (선택도 * Cluster-Factor) 

  1.선택도(SELECTIVITY)는 해당 행이 검색될 수 있는 비율을 의미한다.(조건에 의해 달라짐) 
  2. CLUSTER-FACTOR는 해당 테이블의 행이 몇 개의 블록에 저장되어 있는지를 나타내는 비율 
      ANALYZE TABLE 명령어 수행 후 User_tables에 NUM_ROWS/BLOCKS-EMPTY_BLOCKS 
      컬럼의 값 

 1) ANALYZE TABLE big_emp COMPUTE STATISTICS; 
 2) ANALYZE INDEX I_emp_deptno COMPUTE STATISTICS; 
 3) 선택도= 1/6 
  예): deptno(부서코드)가 6가지 종류가 있을때, where deptno = '3'이라고 했다면, 
      결국 6개중의 하나를 선택했으므로 1/6이된다. 
 Cluster-Factor= 80(800개의 행, 블록당 10개의 행)이면 


선택도(SelectVity) 
  : 내가 어떤 조건들을 부여했을때 데이터가 선택될수 있는 확률 

  예제) Selectivity 는 1에 가까울수록 실행속도가 빠르다. 

  1) Unique-Key/Primary-Key의 경우 
    SELECT * FROM emp WHERE empno = 200; 
 => Selectivity = 1 (좋은 선택도) 
      이유 : Unique-Key 거나 Primary-Key는 유일한 값으로 하나밖에 나올수 없는 값이기 때문에 
            Selectivity = 1 

  2) 2) Non Unique-Key의 경우 
    SELECT * FROM emp WHERE ename = ‘SMITH’; 
 => Selectivity = 1 / distinct-keys => 1/ 4 = 0.07 (좋은 선택도) 
      이유 : emp테이블에 enme이 전부 4명이 있다면.. 그 확률상 
            1/4이 되어서 Selectivity = 0.07이 된다. 

  3) 값을 가진 비동등 조건식의 경우 
  SELECT * FROM emp WHERE empno < 200; 
 => Selectivity = (범위값 ? 최소값) / (최대값 ? 최소값) 
 = (200 ? 1) / (29999 ? 1) = 199 / 29998 = 0.007 
      이유 : 데이터가 29999건이 들어있는 테이블이라 가정한다.. 

 SELECT * FROM emp WHERE empno BETWEEN 100 AND 200; 
 => Selectivity = (최대조건값 ? 최소조건값) / (최대값 ? 최소값) 
 = (200 ? 100) / (29999 ? 1) = 100 / 29998 = 0.003 

    4) 바인드 변수를 가진 비동등 조건식의 경우 
 SELECT * FROM emp WHERE empno < :a ; 
 => Selectivity = 25 % (나쁜 선택도) 
 이유 : 바인드변수를 사용했다면 29999건이 있다고 가정한다면 바인드 변수의 
              값이 무엇이 나올지 알수 없어서 오라클 서버가 가정치(추정치)를 선택하는데 
              Defalut값이 25%가중치를 선택한다. 

 SELECT * FROM emp WHERE empno BETWEEN :a AND :b ; 
 => Selectivity = 50 % (나쁜 선택도) 

============================================================================== 
 비용계산 COST = ( 1/6 * 80 ) = (0.17 * 80) = 14 
    결국 이 조건은 14번의 access로 원하는 결과를 얻을 수 있다. 


Posted by redkite
, |

1. DELETE 

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

  - DML(Data Manuplation Language) 
  - 사용 : DELETE FROM 테이블명 WHERE 조건; 

  - 데이터의 일부 또는 전부를 삭제 할 수 있음 
  - 삭제할 데이터들은 디스크에서 메모리로 로딩(loading)한 후 작업 
  - Disk I/O가 많이 발생하므로 메모리(Data buffer cache)에 있는 데이터를 삭제하기 전에 
    삭제할 데이터에 해당하는 Redo data, Undo Data를 생성 
  - COMMIT하기 전 ROLLBACK이 가능 
  - 삭제할 데이터를 해당 블록 내에서 하나씩 찾아서 삭제하기 때문에 시간이 오래 걸림 
  - 모두 삭제하였을 때도 사용된 블럭의 EXTENT 및 HWM을 그대로 유지  
  - 삭제(delete)하는 테이블의 데이터를 참조(reference)하는 테이블의 데이터에서 
    row단위로 체크해서 참조하는 테이블에 row가 없으면 삭제 가능 
  - 삭제한 row의 수를 반환함 
  - delete로 삭제된 데이터는 FLASHBACK QUERY로 복구 가능 

  - 통계정보(Analyze)를 변경하지 않음 

  

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

2. TRUNCATE 
------------------------------------- 

  - DDL(Data Definition Language) 
  - 사용 : TRUNCATE TABLE 테이블명; 

  - 데이터의 일부 삭제는 불가, 전부 삭제됨 
  - DDL(Data Definition Language) 이므로 데이터를 메모리로 로딩(Loading)하지 않음 
  - Undo, Redo log를 생성하지 않음 

    (테이블이나 인덱스가 차지하고 있던 저장공간 관리영역에 대한 적은 량의 redo log는 생성함) 
  - Undo, Redo log를 생성하지 않기 때문에 ROLLBACK 불가, 복구 불가 
  - 타 테이블에서 참조하고 있으면 Truncate 불가 
  - 모두 삭제되기 때문에 사용된 EXTENT 및 HWM을 초기화 
  - 참조(reference) 제약이 ENABLE인 경우 ORA-02266 오류 발생 
  - 실제 해당 데이터를 일일이 찾아서 지우지 않고, 

      데이터 딕셔너리(Data Dictionary)에 삭제 표시 및 
      빈 공간 표시만을 하기 때문에 빠른 시간 내에 시스템 부하없이 데이터를 삭제 가능 
  - 삭제한 row의 수를 반환하지 않음 
  - Truncate한 Table 하나만을 특정 시점으로 복구 시 Archive Log Mode로 운영 중인 경우에 

      Hot Backup 또는 Cold Backup을 이용한 별도 데이터베이스에서 Incomplete Recovery를 

      한 후 해당 테이블을 exp/imp해야 복구 가능 
  - truncate table로 삭제된 데이터는 FLASHBACK QUERY로도 복구 불가 

    (ORA-01466: unable to read data - table definition has changed) 

  - 통계정보(Analyze)를 변경함

Posted by redkite
, |

NLS_LANG =   [언어]_[영역].[캐릭터셋]
             <language>_<Territory>.<client characterset>
    ex) AMERICAN_ARERICA.KO16KSC5601, AMERICAN_AMRRICA.UTF8
  ● <Territory> - 영역정보, 달력 설정 방법, 통화기호(달라), 숫자그룹
                   날짜 포맷(한국 - "05/08/12 오후 07:28:03", 체코 - "10.08.05 19:28:03")
                   Language 값만 설정하면, Language 값을 따른다. ( AMERICAN ==> AMERICA )
  ● <language> - 캐릭터셋, 정렬방식, 날짜 표기에 사용되는 기호(년/월/일, YYYY/MM/DD) 
                  Default 값은 AMERICAN 이다.
                  실제 날짜 표기(TO_CHAR,TO_DATE 실행시 - 월요일, MON 으로 표시를 좌우하는
                  NLS_DATE_FORMAT 값은 NLS_LANGUAGE 을 따른다. [하단.  테스트 1 참조 ]
  ● <client characterset>
◇ 기본적으로 Server characterset 과 동일하게 설정된다.
    - 클라이언트에서 캐릭터셋만 설정하면, 디폴트로 AMERICAN_AMERICA 로 Territory_language 값이 설정된다.
    ◇ Client Characterset 을 Server 와 다르게 설정 상태에서,
      SQL> SELECT SYS_CONTEXT('USERENV','LANGUAGE') "NLS SET" from dual;
      을 실행 하여도, 항상 CharacterSet 은 항상 서버 CharacterSet 값을 가져온다.
    ◇ client characterset을 서버와 다르게 하는 경우는 크게
      1. 다른 캐릭터셋 DB로 데이타 이전시(EXPORT/IMPORT)
         △  EXPORT/IMPORT 시에는 반드시 서버의 Character Set 동일하게 설정 후 Export/Import 한다.
         △  다른 캐릭터셋의 DB(데이타가 깨지지 않고, 지원되는 경우, SubSet Characterset 일경우)로 IMPORT 시에는
           Import 대상의 DB 캐릭터셋으로 환경 설정 후, Export/Import 한다.
      2. Server(UTF8) - Client(KO16KSC5601)
         △ 다국어 지원되는 데이타베이스에다 한글 입출력 하기 위해서는
            입력시 - 내가 입력 하는 데이타의 캐릭터셋이 한글임을 알리기 위해서,
            출력시 - Unicode 의 Data를 한글로 표현해 달라고 하기 위해서
            지금까지 DB 가 UTF8 이면, Client 도 UTF8 로 설정하면 되는 줄 알았는데. 아니네요 ㅡ_ㅡ;
     ◇ NLS_LANG 지정 방법
     1. SESSION LEVEL
      - 가장 우선 된다.
      - select * from nls_session_parameters 를 통해서 확인 가능
      - 변경 방법1.
      -- 확인
      SQL> select * from nls_session_parameters
           where PARAMETER in  ('NLS_LANGUAGE','NLS_TERRITORY','NLS_DATE_FORMAT','NLS_DATE_LANGUAGE','NLS_SORT');
                  PARAMETER                      VALUE
          ------------------------------ ----------------------------------------
          NLS_LANGUAGE                   AMERICAN
          NLS_TERRITORY                  AMERICA
          NLS_DATE_FORMAT                DD-MON-RR
          NLS_DATE_LANGUAGE              AMERICAN
          NLS_SORT                       BINARY
      SQL> SELECT SYS_CONTEXT('USERENV','LANGUAGE') "NLS SET" from dual;
      NLS SET
      --------------------------------------------------------------------------------
      AMERICAN_AMERICA.US7ASCII
        -- 변경 
        SQL> ALTER SESSION SET NLS_LANGUAGE='KOREAN';
        Session altered.
        -- 변경 확인
        SQL>  select * from nls_session_parameters
        where PARAMETER in  ('NLS_LANGUAGE','NLS_TERRITORY','NLS_DATE_FORMAT','NLS_DATE_LANGUAGE','NLS_SORT');
          PARAMETER                      VALUE
          ------------------------------ ----------------------------------------
          NLS_LANGUAGE                   KOREAN
          NLS_TERRITORY                  AMERICA
          NLS_DATE_FORMAT                DD-MON-RR
          NLS_DATE_LANGUAGE              KOREAN
          NLS_SORT                       BINARY
        SQL> SELECT SYS_CONTEXT('USERENV','LANGUAGE') "NLS SET" from dual;
        NLS SET
        ---------------------------------------------------------------------------------------
        KOREAN_AMERICA.US7ASCII
      - 변경 방법2.
        UNIX, WINDOWS 에서 NLS_LANG 값을 변경한다.
     ◇ UNIX - export NLS_LANG=American_America.US7ASCII 설정
     ◇ Windows - SET NLS_LANG=American_America.US7ASCII 설정
     ◇ NLS_LANG 잘못 지정시
             NLS_LANG 을 잘못 지정시 sqlplus 실행시 오류 발생 [ I 가 하나 빠짐 ]
             export NLS_LANG=American_America.US7ASCI
             sqlplus scott/tiger
             SQL*Plus: Release 8.1.7.0.0 - Production on Thu Jun 4 18:00:25 2009
             (c) Copyright 2000 Oracle Corporation.  All rights reserved.
             ERROR:
             ORA-12705: invalid or unknown NLS parameter value specified
      -
   2. INSTANCE LEVEL
    - SELECT * FROM NLS_INSTANCE_PARAMETERS ; 를 통해서 확인 가능
    - SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME LIKE '%nls%'; 를 통해서 확인 가능
      [ NLS_INSTANCE_PARAMETER 에서 값을 가져온다. ]
   3. DB LEVEL
    - SELECT * FROM NLS_DATABASE_PARAMETERS ; 를 통해서 확인 가능
    - SELECT NAME, VALUE$ FORM SYS.PROPS$ WHERE NAME LIKE '%NLS%' ; 를 통해서 확인 가능
      [ NLS_DATABASE_PARAMETERS 에서 값을 가져온다. ]
    - SELECT * FROM V$NLS_PARAMETERS 를 통해서 확인 가능      
      [ NLS_DATABASE_PARAMETERS 에서 값을 가져온다. ]   
    -      
[테스트 1]
-- NLS_LANGUAGE 을 변경하고, DATE TYPE 의 데이타를 TO_CHAR 함수를 통해서 조회한다.
-- NLA_LANGUAGE, 엄밀히 말해서 NLS_DATE_LANGUAGE 값이 적용되는 것은
-- 요일 정보가  월요일, MON 으로 표기 되는 경우는 TO_CHAR,TO_DATE 사용 시에만 적용됩니다.
-- 현재 NLS_LANGUAGE 및 NLS_DATE_LANGUAGE 체크  
select * from nls_session_parameters
where PARAMETER in  ('NLS_LANGUAGE','NLS_TERRITORY','NLS_DATE_FORMAT','NLS_DATE_LANGUAGE','NLS_SORT');
--  NLS_LANGUAGE        AMERICAN
--  NLS_TERRITORY        AMERICA
--  NLS_DATE_FORMAT        DD-MON-RR
--  NLS_DATE_LANGUAGE    AMERICAN
--  NLS_SORT            BINARY
--  Client NLS_LANG Setting 값 없이 Database 에 Login 하면,
--  DB Creation 시의 Default 값이, 즉 NLS_LANGUAGE[AMERICAN],NLS_TERRITORY[AMERICA] 값을 가지게 된다.
--  또한 NLS_DATE_LANGUAGE[AMERICAN] 값은 특별히 지정하지 않는 경우 ,
--  NLS_LANGUAGE[AMERICAN] 값을 상속한다.
-- 데이타 조회(변경 없음)
SELECT HIREDATE FROM SCOTT.EMP WHERE  EMPNO = 7369 ;
-- 1980/12/17 00:00:00
-- TO_CHAR 를 사용해서 데이타 조회, DEC !!
SELECT TO_CHAR(HIREDATE,'YYYYMONDD') FROM SCOTT.EMP WHERE  EMPNO = 7369 ;
-- 1980DEC17
-- NLS_LANGUAGE 값을 KOREAN 으로 변경
ALTER SESSION SET NLS_LANGUAGE='KOREAN';
select * from nls_session_parameters
where PARAMETER in  ('NLS_LANGUAGE','NLS_TERRITORY','NLS_DATE_FORMAT','NLS_DATE_LANGUAGE','NLS_SORT');
--NLS_LANGUAGE        KOREAN
--NLS_TERRITORY        AMERICA
--NLS_DATE_FORMAT   DD-MON-RR
--NLS_DATE_LANGUAGE    KOREAN
--NLS_SORT            BINARY
===> NLS_LANGUAGE 값이 KOREAN 으로 변경되면, NLS_DATE_LANGUAGE 값도 KOREAN 으로 변경된다.
-- 데이타 조회(변경 없음)
SELECT HIREDATE FROM SCOTT.EMP WHERE  EMPNO = 7369 ;
-- 1980/12/17 00:00:00
-- TO_CHAR 를 사용해서 데이타 조회, 12월
SELECT TO_CHAR(HIREDATE,'YYYYMONDD') FROM SCOTT.EMP WHERE  EMPNO = 7369 ;
-- 198012월17

Posted by redkite
, |

Oracle FAQ's

V$% views return data from memory structures. Data is lost when the instance is restarted.
DBA_% tables return data from the database's data dictionary (SYSTEM tablespace). Data persists across instance restarts.

v$ view는 memory structure data를 보여주며, dba_ table(?)은 system tablespace 상의 data dictoinary data를 보여줍니다. 10g에서 awr 관련 view들이 만들어져서 dba_hist view 등에서 memory structure statistic data를 system tablespace에 저장해서 보여주기도 하죠.
다음의 HP's oracle blog에는 좀더 자세한 내용이 실려 있습니다..
HP's Oracle Blog

V$ & DBA_
Static Data Dictionary Views and Dynamic Performance Views are the core of database administration. They are of crucial importance. Whoever wants to learn Oracle database administration has to get familiar with these views.
The Data Dictionary tables contain meta data on objects, privileges, roles, users etc.
Whenever you issue DML statements Oracle consults its data dictionary to make sure a table is there, it has the right columns and you have privileges on it. And whenever you issue DDL statements Oracle updates its data dictionary.
All information needed by an instance on its database is kept in the data dictionary. Except some storage related information which is in the datafile headers and some information in controlfile and spfile which is needed at instance startup.
At runtime the oracle instance stores information about itself, about its CPU, memory or I/O performance, wait statistics and everything else which is going on at the instance in dynamic performance tables. Dynamic performance tables are non persistent. You can’t see past instance startup. After shutdown or instance crash all information contained in dynamic performance tables is lost. *1)
Through static dictionary views users can get at information kept in data dictionary tables.
And through dynamic performance views users can look at non persistent runtime information contained in dynamic performance tables.
But why are those views that important, you might ask? All information necessary for database administration can be found in those static dictionary views. And basically all information necessary for instance diagnostic and tuning can be obtained through those dynamic performance views at runtime.
Of course, GUI tools like Enterprise Manager or SQL Developer offer the same information
in a more user friendly manner. But all those GUI tools rely on static dictionary views and dynamic performance views.
As a DBA you will likely get into situations where there is no GUI tools available. At times you will be alone with your SQL*Plus skills. Your mastery of data dictionary and performance views will make all the difference!

Posted by redkite
, |

데이터베이스 전문가가 되기 위해 무엇을 준비해야 하는가? 

 

저 자 : 이상원

 

필자의 지식과 능력의 한계와 짧은 지면 관계로 본 원고에서 “DB 전문가가 되기 위해 무엇을 준비해야 하는가?”라는 질문에 충분한 답을 제공하지 못한다. 다만 전산학의 종합 예술가라는 자부심을 갖고 데이터를 깊이 있게 이해하고, DB 핵심 기술에 대한 탄탄한 이해를 바탕으로 특정 DBMS의 아키텍처를 이해하고 다양한 기술들을 활용해서 주어진 문제를 최적화하고 또 최적화한다는 자세로 자신의 내공을 쌓아가면 언젠가는 전문가의 길에 도달할 것이라 믿는다.

‘DB 전문가가 되기 위해서 무엇을(what), 어떻게(how) 준비해야 하는가’라는 질문에 대답하기 전에, 향후 10년 사이에 여전히 DB 전문가가 필요한 것인지, 개인의 미래 직업으로서 왜(why) DB 분야가 매력적인지에 대한 대답이 선결되어야 한다. 한 기술 분야에 있어서 전문가가 필요하다는 것은 그 분야의 기술이 고도로 발달하고 세분화되어 있어서 전문가와 비전문가에게 똑같은 일을 시켰을 때 그 차이(성능, 안전성 또는 생산성 등의 측면에서)가 현격히 나기 때문일 것이다. 

여전히 DB 전문가는 필요한가?
그럼 30년 이상 지속되어온 DB 분야에서 전문가와 비전문가의 차이가 여전히 존재하는 것일까? 필자의 생각으로는 ‘그렇다’이다. 심지어는 전문가와 비전문가의 격차가 앞으로 더 커질 것이라고 본다. 지금까지 DB 애플리케이션은 상대적으로 적은 양의 데이터, 단순한 질의 패턴, DBMS에서 제공하는 기능의 단순성 등으로 인해 전문가와 비전문가가 구사하는 방법의 차이점이 그리 크지 않았을 수도 있다. 하지만 최근의 DB 애플리케이션 분야는 대용량의 데이터, 복잡한 질의, DBMS 기능의 획기적인 발전 등으로 인해 애플리케이션 개발부터 성능 관리, DB 관리 등의 전 분야에 걸쳐서 데이터를 잘 이해하고 DBMS의 신기능을 100% 활용할 수 있는 사람과 그렇지 못한 사람의 차이는 점점 더 커질 수밖에 없다. 
이와 같은 기술적인 측면 이외에 DB 전문가가 필요하다고 판단되는 중요한 이유가 있다. 기업의 IT화는 궁극적으로 데이터의 생산, 수집, 가공, 그리고 이를 통한 정보의 생성 및 유통으로 인한 기업 생산성, 수익성 등의 극대화에 있다. 따라서 기업 IT화의 시작과 끝은 결국은 DB이다. 그 외의 모든 기술들은 부수적인 기술들이다. 따라서 앞으로는 기업이나 조직의 모든 데이터를 꿰뚫고 관장할 수 있는 사람이 기업의 비전, 전략, 영업/재무/마케팅 등의 모든 활동을 가능하게 하는 핵심 인재가 될 날이 곧 도래한다.

직업으로 DB 전문가는 왜 매력적인가?
그럼 이제 개인의 차원으로 눈을 돌려 개인 직업으로 DB 전문가는 왜 매력적인가 살펴보자. 이 질문에 대해서는 사람마다 판단이 다를 수 있기 때문에 필자 개인의 의견으로 답을 대신하고자 한다. 우선 재미가 있는 분야이다. 어떤 새로운 방법을 통해서 문제를 해결할 때(예를 들어 SQL 튜닝으로 엄청나게 속도가 빨라질 수 있다) 충분한 만족감을 느낄 수 있다. 둘째로, DB 전문가는 주어진 문제에 대해 trade-off를 통해 궁극적으로 최적화된 답을 구하는 사람인데, 이는 전산학 분야에 대한 폭넓을 이해를 필요로 하는 종합 예술 분야이다. 셋째, 상위 20%의 전문가에만 포함될 수 있으면 경제적인 보상도 괜찮은 분야이다. 마지막으로 DB 전문가로서의 높은 진입장벽만 통과하면 그 다음부터 새로운 기술을 follow-up하는 것 자체가 힘들지 않다. 적어도 RDBMS의 기술은 새로운 DBMS 벤더가 나타나거나 새로운 기술 기반의 DBMS가 나타날 염려가 가장 적은 분야라고 확신한다. 

미래의 DB 전문가는 무엇을 준비해야 하는가? 
자 그럼 이제 미래의 DB 전문가를 꿈꾸는 사람들은 무엇을 충실히 준비해야 하는가? 이 대답을 하기 위해서는 구체적으로 ‘어떤 DB 전문가가 되기 위해서는 어떤 내용을 준비해야 하는가’라고 답해야 한다. 그러나 DB 전문가의 길은 여러 종류가 있다. 데이터 모델러, 특정 프로그램 언어 환경에서 DB 애플리케이션 개발자, SQL 튜닝 전문가, DB 성능 전문가, DB 백업을 포함해 다양한 관리를 담당하는 관리자, 데이터에 기반한 CRM 전문가, DBMS를 직접 제작하는 엔지니어 등등, 이 칼럼에서 이들 세부 전문기술별로 무엇을 준비해야 하는가를 답하기는 힘들다. 다만 어떤 세부 전문가를 지향하더라도 DB 전문가를 꿈꾸는 사람은 진부하지만 원리 중심으로 준비를 해야 한다는 것이다. 
그럼 DBMS의 원리는 무엇인가? 많은 대답이 있을 수 있지만 필자에게 DB의 3대 핵심 기술을 꼽으라면 주저 없이 다음의 세 가지를 꼽는다. 우선 관계형 데이터 모델과 SQL 언어이다. 특정 DBMS는 특정 데이터 모델을 기반으로 하게 되는데, 현재의 모든 상용 RDBMS들은 E.F. Codd 박사가 제안한 관계형 데이터 모델(참고자료 ①)을 기반으로 하고, 그 이론적 바탕 위에 만들어진 SQL(Structured Query Language)이라는 언어를 인터페이스로 사용하고 있다. 두 번째의 핵심 기술은 SQL 언어 처리를 위해 지금까지 개발된 질의 처리 방식과 질의 최적화 기술이다. 마지막으로 트랜잭션 처리 기술, 동시성 제어와 회복 기법을 들 수 있다. 데이터베이스는 다수의 사용자가 동시에 공용의 데이터를 접근하기 때문에 동시 접근 제어 메커니즘을 반드시 포함해야 하고(참고자료 ③), 트랜잭션의 처리 도중에 컴퓨터가 다운되는 경우에 미션 크리티컬한 데이터의 손실을 막아야 한다. 이 기술들은 거의 예외 없이 모든 상용 RDBMS에 비슷한 방식으로 구현되어 제공되고 있다. 참고자료 ①,②,③은 각각의 기술들을 가장 처음으로 소개한 학술적인 논문들이다. 그리고 참고자료 ④,⑤,⑥은 대표적인 DB 분야의 입문서이다. 

새로운 DBMS 기술의 경향 
앞에서 제시한 3대 핵심 기술을 어느 정도 이해하고 특정 DBMS에서 이들 기술을 어떻게 제공하는지 이해한 후에는 다음과 같은 DBMS 기술의 동향에도 관심을 가질 필요가 있다. 
거대해지는 SQL. DBMS 벤더별로 새로운 버전을 발표할 때마다 끊임없이 SQL 기능을 확장하고 있다. 이는 다시 말해서 지금은 별도의 소프트웨어 툴로서 존재하지만, SQL 언어가 거대한 블랙홀처럼 시장성이 있다고 판단되면 언제든지 해당 기능을 자사의 SQL로 흡수해서 해당 소프트웨어 툴을 별도의 시장으로 남겨두지 않는다는 점이다. 일례로 OLAP 툴과 관련한 기능이다. 
스스로 튜닝하는 DBMS. 가장 최근의 DBMS 기술의 화두 중의 하나는 스스로 튜닝(self-tuning) 또는 스스로 관리하는(self-managing) 기술이다. 학습하는 옵티마이저, 자동적인 메모리 자원 관리, 자동적인 스토지리 관리 등 이전에는 전문가의 손을 필요로 하던 부분을 DBMS가 지능적으로 알아서 조절해나가는 기능을 추가하고 있다는 점이다. 
DB 전문가는 개별 DBMS에 정통한 전문가이기 이전에 주어진 SQL문의 처리 과정을 논리적으로 그리고 알고리즘적으로 충실히 분석하고 판단할 수 있는 사람을 일컫는다. 시중에는 특정 DBMS 기반의 DB 전문서적들이 흔하고, 많은 사람들은 이 책을 위주로 공부를 하는 것 같다. 하지만 필자가 예로 든 DB 입문서들만큼 상세히 DBMS 기술의 원리, 알고리즘, 다양한 매커니즘들에 대한 수학적인 분석 등을 담고 있는 전문 서적은 거의 없다고 본다. 필자는 DB 분야의 전문가가 되기를 꿈꾸는 사람들에게 대표적인 상용 DBMS를 하나 선택하고, 이들 입문서에 있는 내용이 실제로 해당 RDBMS에서 어떻게 구현되어 제공되고 있는지 착실히 공부하기를 권한다. 여기서 간곡히 부탁하고 싶은 것은 한번 더 원리에 충실하기를 바란다는 점이다.

정리 | 조규형 기자 |jokyu@korea.cnet.com


참+고+자+료

① E. F. Codd, A Relational Model for Large Shared Data Banks, Communications of the ACM, 1970
② Pat Selinger 외, Access Path Selection in a Relational Database Management System, SIGMOD, 1979
③ Jim Gray, Raymond A. Lorie, Gianfranco R. Putzolu, Irving L. Traiger: Granularity of Locks and Degrees of Consistency in a Shared Data Base. IFIP Working Conference on Modelling in Data Base Management Systems 1976
④ Abraham Silberschatz, Henry F. Korth and S. Sudarshan, Database System Concepts(4th edition), McGraw-Hill, 2001
⑤ Raugh Ramakrishnan, Jonathan Gehrke, Database Management Systems, (3rd edition), McGraw-Hill, 2002
⑥ Hector Garcia-Molina, Jenifer Widom, Jeff Ullman, Database Systems: The Complete Book, Prentice Hall, 2003

 

Posted by redkite
, |

데이터베이스 설계의 핵심 개념을 잡아라!

 

경영학과에 입학했지만, 컴퓨터에 자신이 없어 배워보려고 들어갔던 컴퓨터 동아리 때문에 인생이 바뀌었다. 대학원에서 정보시스템(IS) 석사 학위를 취득했고, 실무에서는 여러 프로젝트에서 시스템 구축과 데이터베이스 설계 업무를 맡았다. 최근에는 영진출판사에서 ‘솔라리스 서버 바이블’이라는 책을 내기도 했다. 언젠가는 경영학 교수인 아내와 더불어 이론과 실무가 겸비된 정보시스템 개론서를 써보려고 한다. 
큰 규모의 개발 프로젝트는 예외 없이 데이터베이스가 핵심적인 부분을 차지하고 있다. 데이터베이스를 설계하고 구축하는 것이 전체 시스템을 구축하는 것과 그다지 차이가 없는 경우도 많다. 그러나 중요성에 비해 데이터베이스 설계를 잘 아는 개발자는 예상 외로 많지 않다. 이 글에서는 데이터베이스 설계에 대한 지식이 없는 독자를 대상으로 데이터베이스 설계의 개념과 전체적인 설계 과정을 설명한다. 이 글을 통해 큰 어려움 없이 이번 호 특집 기사의 내용을 이해하는데 도움이 되기 바란다.

요즘에는 어떤 정보시스템을 만들든지 데이터베이스는 거의 빠지는 법이 없다. 그러나 데이터베이스 설계에 대한 개념이 없이 만들다보니 제대로 된 데이터베이스 시스템을 보기는 생각보다 쉽지 않다. 데이터베이스를 사용한다고 해서 자동으로 데이터 처리의 문제점이 해결되는 것은 아니다. 기존 데이터 처리 방식의 구조를 그대로 데이터베이스에 적용한 경우에는 데이터를 담는 그릇만 바뀌었을 뿐 기존 문제점은 그대로 존재하기 때문이다. 기존 문제점을 데이터베이스로 상당 부분 해결할 수 있지만, 이는 데이터베이스를 적절하게 설계했을 때만 해당되는 얘기이다. 


데이터베이스 설계의 기본 철학
기존 데이터 처리 방식의 문제점을 포드 자동차 사례를 통해 알아보자. 1980년대 초 포드의 경영진이 마쯔다 자동차 회사를 방문한 후 엄청난 충격을 받게 된다. 포드에는 북미 지역에만 500명이 넘는 외상매입금 부서의 직원이 있었는데, 마쯔다의 외상매입금 부서에는 단지 5명의 직원만으로 업무를 처리하고 있었기 때문이다. 마쯔다의 효율성에 놀란 포드의 경영진은 자기 회사 외상매입금 부서의 업무처리 전 과정을 면밀히 분석해 보았다. 외상매입금 부서는 주문서 사본, 제품 수령증, 제품 배송장의 세 가지 서류가 일치하면 업무를 진행하는데, 만약 서류 중 일부가 일치하지 않는 경우에는 업무를 중지하고 그 원인을 조사하게 된다. 서류가 일치하지 않는 경우는 그리 많지 않았지만, 원인을 규명하기 위해서 많은 인력이 소요되었다. 사실 포드의 외상매입금 부서의 직원이 많은 것도 이 업무에 상당한 시간을 소비하기 때문이었다. 서류 간의 불일치를 줄이기 위한 최선책은 뭘까? 불일치의 가능성을 사전에 배제하는 것이다. 포드는 데이터베이스를 이용해 데이터간의 불일치를 배제하도록 외상매입금 처리 과정을 개선했고, 그 결과 외상매입금 부서의 생산성을 400% 가까이 향상할 수 있었다.


수작업으로 데이터 처리하기
기존 데이터 처리 방식은 어떤 것이 있을까? 전통적인 방법으로는 수작업으로 데이터를 처리하는 방법을 들 수 있다. 즉, 각 부서마다 장부에 정보를 기록하는 것이다. 부서마다 같은 내용의 문서가 중복해서 존재하기 때문에 자원이 불필요하게 낭비되는 것은 쉽게 알 수 있다. 그러나 그것보다 더 심각한 것은 중복으로 인해 데이터의 일관성이 깨질 수 있다는 것이다. 예를 들어 주문정보가 영업부와 창고 등의 여러 부서에서 보관되고 있는데, 만약 고객이 주문을 변경할 경우 두 부서의 주문 정보를 동시에 변경하지 않는 한 데이터는 일관성을 잃게 된다. 따라서 수작업 방식은 포드의 예처럼 장부 사이에 불일치가 일어날 가능성을 항상 가지고 있게 된다. 


컴퓨터를 이용한 파일 처리
또 다른 데이터 처리 방식으로는 컴퓨터를 이용한 파일 처리 방식이 있다. 파일 처리 방식은 데이터가 파일에 저장되었기 때문에 붙여진 이름이다. 예를 들어 영업부에서는 고객 데이터 파일, 주문 데이터 파일에 정보를 저장하는 주문처리 프로그램을 만들어 활용하고, 창고에서는 고객 데이터 파일, 배송 데이터 파일에 정보를 저장하는 배송처리 프로그램을 만들어 활용하는 식이다. 파일 처리 방식은 수작업 방식을 자동화한 것에 불과할 뿐이지 근본적으로 그 구조를 바꾼 것은 아니기 때문에 수작업 방식에서 존재했던 데이터의 중복은 여전히 남아 있다. 예를 들어 영업부에서 사용하는 고객 데이터 파일과 창고에서 사용하는 고객 데이터 파일이 중복되어 있기 때문에 데이터를 중복 입력하느라 인력을 낭비하는 것은 물론, 같은 데이터가 여러 곳에 독립적으로 보관됨으로써 데이터의 불일치 문제가 발생해 더욱 심각한 문제로 대두되었다. 물론 이런 파일 처리 방식의 문제점은 사용자의 노력으로 줄여 나갈 수 있다. 하지만 더 바람직한 방법은 처음부터 이런 문제점이 발생하지 않는 환경을 만드는 것이다. 오류 가능성을 사전에 배제하는 것, 그것이 바로 데이터베이스의 기본 철학이다.


데이터베이스를 데이터베이스답게 설계하기 위한 지침

데이터 중복을 최소화하라
데이트(Date) 같은 학자는 데이터베이스의 특징으로 통합과 공유를 들었다. 풀어서 얘기하면 데이터베이스란 모름지기 서로 관련 있는 데이터를 최소한의 중복으로 통합해서 여러 사용자가 사용할 수 있도록 해야 한다는 뜻이고, 데이터를 덕지덕지 중복해서 만들어 놓은 것은 데이터베이스라고 하기 힘들다는 뜻이기도 하다.

일반적으로는 데이터 항목의 각 값이 데이터베이스에 중복 없이 한번만 기록될 때 데이터의 일관성을 유지되는데, 이처럼 중복 없이 데이터를 한번만 기록하기 위한 데이터베이스의 설계 이론을 정규화(normalization)라고 한다. 

경우에 따라서는 처리 속도의 향상을 위해서 특정 데이터 항목의 값을 중복해서 기록할 수 있지만, 예외적인 경우이다. 이런 경우에도 데이터베이스 시스템이 데이터의 중복을 통제하도록 설계해야 한다. 예를 들어 고객의 주소를 부득이하게 두 군데 기록했다면, 그중 하나의 주소가 변경되면 다른 주소도 자동으로 변경되게끔 만들어야 한다는 것이다. 데이터의 중복이 있더라도 미리 계획된 것이고, 일관성을 유지할 수 있는 대책이 마련되어 있다면 이를 ‘통제된 중복’이라고 부른다. 그러나 불가피한 경우를 제외하고는 중복을 만들어 통제하기보다 아예 중복이 없게끔 만드는 것이 데이터베이스 설계의 철학임을 다시 강조한다. 


데이터 정의를 표준화하라
파일 처리 방식에 비해 데이터베이스는 데이터가 한 곳에 저장되기 때문에 데이터 항목의 정의, 명칭, 형태 등의 표준화를 하기가 쉽다. 따라서 데이터베이스가 가진 장점을 잘 살리려면 같은 용어를 다른 뜻으로 사용하거나, 다른 용어를 같은 뜻으로 사용하는데서 오는 개발상의 혼란을 사전에 방지하기 위해 용어사전 등을 이용해 데이터 정의를 표준화해야 한다. 

예를 들어, 주문일 속성을 어떤 곳에서는 order_date, 다른 곳에서는 order_day, 또 다른 곳에서는 ordered_date, 혹은 OrderDate처럼 여러 이름으로 나타낸다면 주문일 속성을 이용할 때마다 여기서는 어떤 이름을 사용해야 하는지 고민하거나 설계도를 그때마다 참조해야 한다. 더 좋지 않은 경우는 다른 의미를 가진 속성을 동일한 이름으로 지정하는 경우인데, 이 속성이 자주 사용하는 속성인 경우엔 피해가 막심하다. 숱한 버그 발생으로 개발 기간이 엿가락 늘어지듯 늘어나 버린다. 이렇게 데이터베이스를 만드는 경우엔 제대로 데이터베이스를 설계했다고 볼 수 없다.

데이터 무결성을 위한 기능을 최대한 활용하라
파일 처리 방식에서는 사용자마다 다른 파일을 가지고 있기 때문에 한 파일에 부정확한 데이터가 있더라도 그 파일을 이용하는 사용자에게만 국한된 피해로 끝날 수 있지만, 데이터베이스는 모든 사용자가 동일한 데이터를 사용하기 때문에 일단 잘못된 데이터가 저장된다면 심각한 피해가 발생한다. 따라서 입력 데이터에 대한 철저한 오류 검사를 하도록 데이터베이스를 설계해야 한다. 데이터가 오류가 없는 상태를 데이터 무결성이라고 하는데, 데이터베이스에는 데이터 무결성을 위해 여러 제약을 가할 수 있는 기능이 있다.

예전에는 클라이언트 프로그램으로만 입력이 가능했기 때문에 데이터의 오류를 프로그램에서만 검증해도 어느 정도는 데이터의 무결성이 보장되었다. 하지만 이제는 최종 사용자가 엑셀 등을 사용해서 데이터베이스에 임의로 접근하는 것도 가능하게 되었기 때문에 어떤 프로그램을 사용해서 데이터에 접근하든지간에 비정상적인 데이터는 입력이 되지 않게끔 막아야 할 필요성이 생겼다. 따라서 프로그램에서 데이터의 무결성을 지키기보다는 데이터베이스에서 데이터의 무결성을 지키도록 노력해야 한다.


데이터베이스 설계의 핵심 개념

관계형 데이터베이스
데이터베이스에 저장할 개체와 그 관계를 요약한 것을 데이터 모델이라고 하는데, 계층형(구조적, hierarchical) 데이터 모델, 그물형(네트워크, network) 데이터 모델, 관계형 데이터 모델, 객체 데이터 모델 등이 있다. 현재 업계에서 가장 널리 채택되고 있는 것은 관계형 데이터 모델이다. 관계형 데이터 모델을 채택하고 있는 관계형 데이터베이스는 오라클, 인포믹스, 사이베이스, MS SQL 서버 등 여러분이 들어본 대부분의 데이터베이스가 해당된다. 관계형 데이터 모델의 대표적인 특징으로는 각 행(튜플, tuple)을 유일하게 구분할 수 있는 열(속성, attribute)이 있다는 것이다. 이를테면 회원 테이블은 각 회원을 유일하게 구분할 수 있는 ID 속성을 가지는데, 이런 속성을 주 키(primary key)라고 한다. 또 다른 특징으로는 테이블의 각 셀의 값은 반드시 단일 값이어야 한다는 게 있다. 예를 들어, 어떤 책을 ‘홍길동’과 ‘임꺽정’이라는 저자가 공동으로 썼을 경우 한 셀에 ‘홍길동, 임꺽정’처럼 두 개의 값을 저장하면 안 된다는 것이다. 만약 두 개의 값을 저장하고 싶다면 다음에서 설명할 정규화 과정을 통해 단일 값을 갖는 셀로 바꿔줘야 한다.

제약
모든 데이터 모델은 본질적 제약, 내재적 제약, 명시적 제약을 가진다. 본질적 제약은 데이터 모델의 구조적인 특성으로 인한 제약을 말한다. 그러므로 정의에 따라 반드시 주 키가 있어야 하고 테이블의 각 셀이 단일 값을 갖는다는 관계형 데이터 모델의 특징이 바로 본질적 제약이 된다. 관계형 데이터 모델은 다른 데이터 모델에 비해 비교적 적은 본질적 제약을 가진다. 본질적 제약이 적다는 것은 데이터베이스를 자유롭게 설계할 수 있지만, 그 대신 데이터의 무결성을 유지하고 관계성의 의미를 정확히 표현하기 위해 많은 노력을 하지 않으면 데이터베이스가 엉망이 된다는 의미이기도 하다.

내재적 제약은 데이터의 의미를 정확히 표현하고 오류를 방지하기 위해 데이터베이스에 지정하는 제약이다. 관계형 데이터베이스의 주요 제약으로 영역 제약과 참조 무결성 제약이 있다. 영역 제약은 허용되는 데이터 값에 대한 제약이다. 예를 들어, 고객의 마일리지가 규정상 0 이하로 내려갈 수 없다면, 0 이상의 정수만 입력될 수 있도록 영역 제약을 사용하는 것이다. 참조 무결성 제약은 어떤 데이터에 의해 참조되는 데이터는 항상 존재해야 한다는 제약이다. 예를 들어 주문 테이블에 주문을 한 고객번호가 남아 있는데 정작 고객 테이블에는 그 고객 번호에 해당하는 고객이 없는 경우를 참조 무결성이 깨졌다고 하는데, 참조 키(외부 키, foreign key)를 설정해서 참조 무결성을 보장할 수 있다. 

개발자의 생산성이나 데이터 입력 오류의 가능성을 고려할 때 데이터 무결성은 내재적 제약으로 지키는 것이 가장 바람직하다. 그러나 어떤 데이터 모델도 현실 세계의 모든 제약을 데이터베이스에 내재하는 것은 불가능하다. 이런 제약은 프로그램에서 처리하거나 사용자의 수작업에 의지할 수밖에 없는데 이런 제약을 명시적 제약이라고 한다. 명시적 제약으로 처리해야하는 복잡한 무결성도 프로시저나 트리거 등 서버에서 처리할 수 있는 방법을 동원하는 것이 좋다. 이렇게 되면 업무 규칙이 바뀐다고 해도 데이터베이스 한군데에서만 바꿔주면 되기 때문에 클라이언트 프로그램 모두를 바꿔주는 것보다 훨씬 간편하다. 물론 그렇게 하면 데이터베이스의 부담이 가중되어 전체적인 성능이 나빠지는 경우가 생길 수 있다. 그런 경우에도 프로그램에서 무결성을 보장하는 것이 아니라 애플리케이션 서버에서 무결성을 보장하는 3티어(tier) 구조로 가는 것이 바른 길이다.

정규화
정규화는 데이터베이스에 저장되는 데이터의 중복을 최소화시키는 방법이다. 정규화는 1차, 2차, 3차, 보이스-코드, 4차, 5차, 영역-키 정규화가 있는데, 실무에서는 보통 3차 정규화 또는 보이스-코드 정규화 정도까지 하기를 권장하고 있다.

1차 정규화
1차 정규화는 테이블의 각 셀은 단일 값을 갖는다는 관계형 데이터모델의 본질적 제약과 관련된 개념이다. <표 1>에서 제품번호, 단가, 수량의 속성값은 한 셀에 여러 값이 들어가 있기 때문에 관계형 데이터의 본질적 제약을 위배한 것이다. 

주문번호 주문일 고객번호 고객이름 제품번호 단가 수량
0001 2003.7.15 c1 홍길동 p01
P02
350
100
1
3
0002 20037.16 c2 임꺽정 p01 350 4
00003 2003.7.17 c1 홍길동 p03 400 5

<표 1> 정규화되지 않은 테이블

주문번호 밑에 주키를 나타내는 밑줄을 꼭 그어주세요 1차 정규화를 하기 위해서는 <표 2>처럼 한 셀에 하나의 데이터만을 나타내야 한다. 이렇게 1차 정규화가 된 상태에서는 주문번호 속성이 더 이상 유일한 값을 갖지 못하므로 주문번호와 제품번호를 연결한 합성키가 1차 정규화 상태의 새로운 주 키가 된다.


주문번호 주문일 고객번호 고객이름 제품번호 단가 수량
0001 2003.7.15 c1 홍길동 p01 350 1
0001 2003.7.15 c1 홍길동 P02 100 3
0002 20037.16 c2 임꺽정 p01 350 4
00003 2003.7.17 c1 홍길동 p03 400 5

<표 2> 1차 정규화가 끝난 테이블

주문번호와 제품번호 밑에 주키를 나타내는 밑줄을 꼭 그어주세요 

2차 정규화
2차 정규화 단계부터는 데이터의 중복을 제거하게 된다. <표 2>에서 중복된 데이터를 찾아보자. 제품번호 ‘p01’의 단가가 350이라는 정보가 두 번 중복되어 나타나고 있음을 볼 수 있다. 단가는 제품번호 속성에 종속되어 있기 때문에 제품번호가 정해지면 그 제품의 단가가 정해진다. 따라서, <표 3>처럼 종속관계에 있는 제품번호와 단가 속성을 별도의 [제품] 테이블로 분리하고, 원래의 테이블에는 제품번호 속성만 남겨두면 제품번호 ‘p01’의 단가인 350이라는 정보가 두 번 중복되는 것을 피할 수 있다.

또 다른 중복 정보는 없을까? 주문번호 ‘00001’에 종속되는 주문일, 고객번호, 고객이름이 두 번 중복해서 나타나고 있다. 마찬가지 방법으로 <표 3>처럼 주문번호와 주문일, 고객번호, 고객이름을 별도의 [주문-고객] 테이블로 분리하고, 원래의 테이블에는 주문번호 속성만 남겨두면 특정 주문번호에 대한 주문일, 고객번호, 고객이름 데이터가 두 번씩 중복되는 것을 피할 수 있다. 

이 외에도 중복을 제거할 수 있는 부분이 있다. <표 3>의 [주문-고객] 테이블을 보면 고객번호 ‘c1’의 이름이 ‘홍길동’이라는 정보가 두 번 중복해서 나타나고 있다. 그러나 이것은 3차 정규화에서 처리할 부분이다. 2차 정규화는 주 키의 일부분, 즉 주문번호와 제품번호에 종속된 것만을 대상으로 하는데, 고객번호나 고객이름은 주 키를 구성하는 속성이 아니기 때문이다. 

주 키의 일부분에만 종속된 상태를 부분 종속이라고 하는데, 부분 종속을 갖는 속성을 별도의 테이블로 분리한 <표 3>의 상태가 2차 정규화이다. 따라서 1차 정규화된 상태의 주 키가 하나의 속성뿐이라면 2차 정규화는 건너뛰고 바로 3차 정규화를 하면 된다. 그러나 <표 2>

처럼 1차 정규화된 상태의 주 키가 둘 이상의 속성을 가지고 있을 경우에는 2차 정규화 작업이 필요하다. 

제품

제품번호 단가
p01 350
p02 100
p03 400

<표 3> 2차 정규화가 끝난 테이블


주문-제품

주문번호 제품번호 수량
00001 p01 1
00001 p02 3
00002 p01 4
00002 p03 5


주문-고객

주문번호 주문일 고객번호 고객이름
00001 2003.7.15 c1 홍길동
00002 2003.7.16 c2 임꺽정
00003 2003.7.17 c1 홍길동



3차 정규화
2차 정규화가 주 키 속성과 주 키가 아닌 속성간의 종속관계가 있을 경우 이를 별도의 테이블로 분리한 것이라면 3차 정규화는 2차 정규화가 끝난 상태에서 주 키가 아닌 속성끼리 종속관계가 있을 경우 이를 별도의 테이블로 분리한 것이다. <표 3>은 2차 정규화가 끝난 상태이기 때문에 주 키가 아닌 속성끼리 중복되는 정보가 있는지를 살펴보면 된다. 앞에서 얘기한대로 <표 3>의 [주문-고객] 테이블을 보면 고객번호 ‘c1’의 이름이 ‘홍길동’이라는 정보가 두 번 중복해서 나타나고 있다. 2차 정규화와 마찬가지 방법으로 고객번호와 고객이름을 별도의 [고객] 테이블로 분리하고 원래의 [주문-고객] 테이블에는 고객번호 속성만 남겨두면 3차 정규화가 완성된다.<표 4>는 3차 정규화까지 끝난 상태의 최종 테이블이다.

제품

제품번호 단가
p01 350
p02 100
p03 400

<표 4> 3차 정규화가 끝난 테이블

주문-제품

주문번호 제품번호 수량
00001 p01 1
00001 p02 3
00002 p01 4
00002 p03 5


주문-고객

주문번호 주문일 고객번호
00001 2003.7.15 c1
00002 2003.7.16 c2


고객
고객번호 고객이름
c1 홍길동
c2 임꺽정


보이스-코드 정규화
3차 정규화가 끝난 상태에서도 여러 개의 후보 키(주 키가 될 수 있는 속성)가 존재하는 경우에는 속성간의 종속이 남아있을 수 있다. 이를 제거한 것이 보이스-코드 정규화이다. <표 4>에서는 후보 키가 여러 개 있는 테이블이 없으므로 보이스-코드 정규화를 추가적으로 할 필요가 없다.


데이터베이스 설계 과정

PC를 이용해 한두 명의 사용자가 이용하는 간단한 데이터베이스를 만드는 작업은 구체적인 방법론 없이도 할 수 있다. 그러나 분당 수천, 수만 건의 온라인 거래를 처리하는 데이터베이스의 구축은 간단한 작업이 아니다. 이런 대규모 데이터베이스의 구축은 방법론에서 제시하는 과정을 거쳐야 한다. 데이터베이스 설계 방법론에 따라 세부적인 과정은 조금씩 달라지지만, 어떤 방법론이든 전체적으로는 대략 다음과 같은 과정을 거치게 된다. 


데이터베이스 요구사항 파악 및 해당 업무 분석

데이터베이스 설계를 하려면 우선 요구사항을 먼저 파악해야 한다. 요구사항에는 사용자가 필요로 하는 정보와 출력물 외에도 처리속도나 보안의 정도 등도 포함된다. 이 과정에서 해당 업무를 분석해서 데이터베이스와 관련된 사용자를 파악하고, 각 개체와 개체간의 관계도 파악하고, 업무처리 규칙 등도 도출하게 된다. 이와 같은 사항을 파악하기 위해서 될 수 있으면 많은 자료를 수집하는 것이 좋다. 조직도표, 업무기술서, 각종 보고서, 입출력 양식, 시스템 화면 등은 좋은 자료가 된다. 


논리적 설계

요구사항 분석이 끝나면 논리 데이터베이스를 설계하게 된다. 논리적 설계는 실제 데이터베이스 관리시스템(DBMS)에 최적화된 설계를 하는 단계가 아니므로 컬럼(필드) 길이나 데이터 타입 같은 상세한 사항까지 설계하지 않아도 무방하다. 데이터베이스의 설계도 격인 개체-관계 다이어그램(E-R 다이어그램)을 손으로 그리는 것도 가능하지만 한정된 시간 안에 충실한 설계를 하기 위해 이 단계에서부터 데이터베이스 설계를 위한 케이스툴(Case Tool)을 사용하는 게 보통이다.


개체 지정
논리적 설계의 첫 번째 단계는 요구사항 분석 단계에서 수집한 자료를 가지고 개체를 정하는 것이다. 업무에서 관리되고, 일정한 자신만의 속성을 여러 개 가진 집합적인 성격의 것을 개체(정확하게 말하면 개체집합)로 고르면 된다. 이 때 개체와 개체의 속성을 혼동하지 말아야 한다. 이를테면 주민등록번호는 회원이라는 개체의 속성이기 때문에 주민등록번호를 개체로 정하면 안 된다. 나중에 물리적 설계 단계에서 개체는 테이블로, 개체의 속성은 칼럼으로 변환한다.


관계 지정
개체 지정이 끝나면 각 개체간에 어떤 관계가 있는 지를 지정한다. 관계를 그림으로 나타내는 방법에는 여러 가지가 있는데, 그 중 정보공학방법론(Information Engineering Methodology)에서 주장하는 방법을 위주로 설명을 하겠다. 관계는 업무기술서나 기타 자료에서 정확하게 표현되지 않는 경우가 많으므로 내용을 보고 관계를 유추해서 작업하는 경우가 대부분이다. 예를 들어 회원과 주문간의 관계를 유추한 내용이 다음과 같다고 하자.

◆ 각 회원은 여러 개의 주문을 신청할 수 있다. 물론, 한번도 주문하지 않은 회원도 있을 수 있고, 한번만 주문한 회원도 있을 수 있다.
◆ 각 주문은 그 주문을 신청한 회원이 반드시 한명 있다.
이 관계를 E-R 다이어그램으로 나타내면 <화면 1>과 같다.

주문과 회원의 관계

회원 한명의 입장에서 볼 때 주문을 한번도 하지 않을 수 있고(?), 한번만 주문할 수도 있고(?), 여러 번 주문할 수도 있다(>)는 것이다. 보통 이런 경우를 1대다 (엄밀하게는 1대 0, 1, 다) 관계라고 말하곤 한다. 반대로 하나의 주문 입장에서 볼 때, 그 주문을 한 회원은 반드시 한명만 있어야 한다(?)는 뜻이다. ?이나 < 표시가 없으므로 주문한 회원이 없어서도 안 되고, 주문한 회원이 둘 이상일 수도 없다. 다른 개체들간의 관계도 그려보면 <화면 2>와 같다. 거의 대부분의 관계는 1대다 관계이다.

<화면 2>

다른 개체들간의 관계

개체간의 관계를 가지고 <화면 2>처럼 나타낼 수도 있어야 하지만, 반대로 <화면 2>를 보고 개체간의 관계를 해석할 수도 있어야 한다. 예를 들어, 상품과 장바구니의 관계를 해석하자면 다음과 같다.

◆ 하나의 상품은 여러 장바구니에 담길 수도 있고(∧), 하나의 장바구니에만 담길 수도 있고(?), 장바구니에 전혀 안 담길 수도 있다(?). 
◆ 하나의 장바구니는 반드시 하나의 상품만을 담아야 한다(?).
관계를 설정했을 때 1대다 관계가 아닌 것이 생길 수도 있다. 예를 들어 한 학생이 여러 강의를 수강할 수 있고, 한 강의는 여러 수강생을 등록받을 수 있다는 업무규칙을 표현하면 <화면 3>과 같이 다대다 관계가 된다.

<화면 3>


다대다 관계의 예

이런 다대다 관계는 두 개체의 주 키를 합친 것을 주 키로 하는 개체를 새로 만들어 1대다 관계로 변환해준다. 예를 들어 <화면 3>은 <화면 4>와 같이 변환해주면 된다.

<화면 4>


다대다 관계를 해소한 E-R 다이어그램

어떤 경우에는 1대1 관계가 생길 수도 있다. 1대1 관계가 있을 때는 두 개체를 하나의 개체로 통합할 수 없는지 생각해본다. 두 개체를 하나로 합쳐도 무리가 없다고 판단되면 하나의 개체로 통합을 한다.


주 키 지정
관계를 설정한 다음에는 각 개체의 속성 중에서 주 키를 지정한다. 주 키는 <화면 5>의 장바구니에서처럼 여러 속성이 모여서 이루어질 수도 있다. 주 키가 없는 경우에는 자동증가 컬럼 등을 만들어서 주 키로 지정한다. 주 키가 있더라도 너무 많은 속성이 모여서 이루어진 경우에는 자동증가 컬럼 등을 만들어서 주 키로 지정하고, 원래의 주 키는 후보키로 활용하는 것이 좋다.

<화면 5>


주 키를 지정한 E-R 다이어그램

이 단계에서부터는 용어사전을 만들어서 활용해야 한다. 용어사전은 (주 키/후보 키를 포함한) 모든 속성의 이름, 그 속성의 물리 이름(물리적 설계 때 사용할 컬럼 이름), 용어의 뜻을 정리해놓은 것이다. 한사람이 데이터베이스 설계를 한다면 용어사전을 생략할 수도 있겠지만, 여러 명이 데이터베이스 설계를 할 때는 반드시 용어사전을 만들어서 활용해야 한다. 어떤 속성의 이름을 지정할 필요가 있을 때는 먼저 용어사전을 뒤져보고, 그 속성의 뜻에 해당하는 이름이 사전에 있다면 그 이름을 사용한다. 만약 용어사전에 그 뜻에 해당하는 이름이 없다면 용어사전에 그 뜻에 해당하는 속성의 이름 등을 추가해준다. 


속성 지정
주 키/후보 키 지정이 끝나면 나머지 속성을 지정한다. 업무에 필요한 속성이 빠진 게 없는지 꼼꼼히 따져봐야 한다. 이 때, 업무에 필요 없는 속성을 마구잡이로 추가하지 않도록 주의한다.


정규화
각 개체에서 정규화할 것은 없는지 확인한다. 정규화에 대해서는 앞에서 설명했으므로 여기에서는 설명을 생략한다.


개체의 통합
하나의 테이블에는 데이터를 추가, 삭제, 조회, 수정하는 4개의 프로그램이 필요하다. 따라서, 개체의 수가 많아지면 많아질수록 개발해야 하는 프로그램의 양도 많아진다. 그리고 많은 테이블을 머리 속에 담고 작업하기에는 인지적인 무리가 따르므로 하나로 통합할 수 있는 개체는 통합해서 개수를 줄이는 것이 좋다. 통합의 고려 대상이 되는 개체는 1대1 관계에 있는 개체와, 서로간에 비슷한 속성을 가진 개체이다. 예를 들어, 비슷비슷한 게시판 개체가 여러 개인 경우, 주제별로 개체를 따로 만들 것이 아니라 게시판이라는 하나의 개체로 모두 통합하는 것이 바람직하다.

여기서 주의할 점은 개체 수를 줄이는 것이 모든 것에 우선하는 가치는 아니라는 점이다. 정규화 과정을 거치게 되면 개체 수가 늘어나게 되는데 개체 수가 늘어나는 게 두려워 정규화를 하지 않는다는 것은 웃기는 일이다. 나눠야 하는 것은 반드시 나누되, 굳이 나눌 필요가 없는 것은 하나로 통합하라는 의미이다.


물리적 설계
논리적 설계가 마무리되면 특정 DBMS에 맞춰 물리적 설계를 진행한다. 따라서 물리적 설계를 진행하기 이전에 DBMS가 결정되어 있어야 한다. 물리적 설계에서 해야 할 일은 테이블과 컬럼의 이름을 지정하고 각 컬럼의 데이터 타입 등을 지정하는 일이다. 이외에도 트랜잭션 분석, 뷰 설계, 인덱스 설계, 용량 설계, 보안 설계와 같은 중요한 과정이 포함된다. 물리적 설계를 어떻게 하느냐에 따라 데이터베이스의 성능에 많은 차이가 발생하므로 논리적 설계 못지않게 중요한 과정이다.


테이블과 컬럼 이름 지정
케이스툴을 사용하면 손쉽게 논리 데이터베이스를 특정 DBMS에 맞게 변환해준다. 다만, 이 과정에서 한글로 된 개체 이름과 속성 이름이 변경 없이 그대로 테이블과 컬럼에 사용되는데, 이를 영문으로 바꿔주는 작업은 직접 해야 한다. 여러 가지 이유 때문에 실제 테이블이나 컬럼 이름에는 영문이나 숫자만 써주는 것이 좋다. 숫자가 제일 앞에 오거나, 공백이 이름 중간에 오는 경우도 문제를 발생시킬 소지가 있으므로 피하는 게 좋다. 테이블이나 컬럼 이름을 지정할 때도 꼭 용어사전을 사용해서 중구난방으로 이름이 붙여지지 않게 주의해야 한다.


각 컬럼의 데이터 타입 및 제약 지정
테이블과 컬럼의 이름을 적절하게 변환했다면 각 컬럼의 데이터 타입과 길이, 초기 값, 데이터 무결성을 위한 제약 등을 지정한다. 


트랜잭션 분석
데이터베이스에 무슨 트랜잭션이 어느 정도 발생하는지를 분석하고 그에 따라 트랜잭션 처리 방법을 결정한다.


뷰 설계
다양한 사용자 관점에서 데이터를 보여주기 위해서, 혹은 보안상의 문제를 고려하여 뷰를 설계한다. 


인덱스 설정
개발 단계에서는 데이터의 양이 그리 많지 않기 때문에 인덱스가 없어도 속도 문제가 나타나는 경우가 별로 없다. 그러나 실제 서비스에 들어가서 많은 사용자가 시스템을 이용하기 시작하면 필연적으로 속도 문제가 불거지게 되므로 데이터베이스를 만들 때부터 인덱스를 신경써야 한다. 특히 참조키에 인덱스 설정을 하지 않는 경우가 종종 있는데, 참조키에는 무조건 인덱스를 설정하기 바란다. 이 외에도 인덱스를 설정하는 데 참고할만한 많은 가이드라인이 있으므로 꼭 관련 서적을 찾아보기 바란다.


용량 설계
데이터의 입출력 양과 저장해야 할 데이터의 양을 예측하고 이에 맞는 하드웨어를 선택한다. 그리고, 데이터의 입출력 성능을 향상시킬 수 있도록 하드디스크를 적절히 분리해서 설계한다.

대략적으로 물리적 설계에서 진행되는 과정은 이와 같다. 이외에도 각종 튜닝 과정이 추가되는데, 물리적 설계 단계의 몇몇 과정, 즉 뷰, 인덱스, 용량 설계 등과 더불어 튜닝과정은 설계할 때 한번으로 끝나는 작업이 아니라, 개발 중이나 개발 후에도 반복적으로 모니터링하면서 진행해야 하는 작업들이다.


어떻게 공부할 것인가?
시스템 구축이라고 부를만한 큰 규모의 개발 프로젝트에서는 데이터베이스가 핵심적인 부분을 차지하고 있다. 그 중에서도 데이터베이스 설계는 핵심 중의 핵심이다. 그 증거로 데이터베이스 모델러가 전체적인 IT 시스템 컨설팅을 담당하는 사례가 빈번하다는 것만 들어도 충분할 것이다. 데이터베이스 설계가 그만큼 중추적인 역할을 하기 때문에 실제 프로젝트에서는 데이터베이스만이 아니라 전체 프로젝트를 분석하고 이끌어갈 수 있는 데이터베이스 모델러를 요구한다.

초보자가 데이터베이스 모델러가 되고자 한다면 우선 논리적 설계에 대한 이론을 공부해야 한다. 논리적 설계에 대한 이론이 기본이 된 후에 자신의 해당 업무부터 분석하고 설계하는 작업을 수행해보자. 초보자가 흔히 빠지기 쉬운 함정은 업무를 파악하기도 전에 바로 데이터베이스를 설계하는 것인데, 성공적인 설계를 하기 위해서는 해당 업무에 관한 한 현업 담당자보다 더 많은 업무 지식을 가지고 있다고 자신할 정도가 되어야 한다. 이렇게 파악한 업무 지식을 바탕으로 논리적 설계를 충분히 해보기 바란다. 물리적 설계 공부는 논리적 설계 경험을 충분히 쌓아서 자신이 붙었을 때 시작해도 늦지 않다. 물리적 설계는 구체적인 DBMS와 관련이 있기 때문에 실제로 테스트하면서 튜닝해보는 노력이 필요하다.

이것으로 충분한가? 아니다. 한정된 시간 안에서 충분한 설계를 하기 위해서는 설계의 질도 중요하지만 설계의 생산성도 결코 무시할 수 없다. 케이스툴과 오피스 프로그램을 공부해서 각종 설계 산출물을 설계가 끝남과 동시에 자동으로 생성할 수 있도록 해야 한다. 시간이 없어서 문서화를 하지 못한다거나, 수작업으로 문서화를 하려다 충분한 설계를 못한다면 데이터베이스 모델러로서의 자질 미달이다. 

데이터베이스 설계는 전체 프로젝트와 밀접한 관련이 있기 때문에 다른 사람의 업무에 관여를 하지 않을 수 없다.

그러다보면 직간접적으로 전체 프로젝트 진행에 대한 지식을 쌓기에도 좋다. 따라서 옵션사항이긴 하지만, 정보공학방법론이나 객체지향방법론 같은 프로젝트 방법론에 대해서도 공부할 수 있다면 금상첨화겠다. 아무쪼록 이번 특집을 접하는 독자들이 열심히 노력해서 데이터베이스 설계와 더불어 전체 프로젝트까지 아우르는 전문가가 되었으면 한다.

참고 자료 
◆ Robert Orfali, Dan Harkey, Jeri Edwards :

    'Client/Server Survival Guide', 3rd edition, John Wiley & Sons
◆ 서길수 : '데이터베이스 관리', 제 2판, 박영사
◆ 이춘식 : '데이터베이스 설계와 구축', 한빛미디어

Posted by redkite
, |

데이터베이스 전문가가 되고자 하는 사람들을 위하여

 

사람들은 SQL 서버를 어떻게 시작했을까? 항상 궁금했던 사항이지만 아마 대부분의 사람들을 몇 가지 방식들로 구분할 수 있을 것이다. 내가 말하는 것들과 여러분들이 겪었던 것과 얼마나 다른지 생각해보았으면 한다.

 

필자가 생각했을 때 가장 큰 구분은 그 사람이 대학 때 관계형 데이터베이스 학문을 배운 적이 있는가 없는가 하는 것이다. 우리나라 대학 학부에서 관계형 데이터베이스 학과나 SQL 서버 학과니 하는 것은 없는 것으로 알고 있다. 아마도 정확히는 모르지만 데이터베이스 학과도 없을 것이다. 그러므로 이러한 전공 여부는 Computer Science나 Programming 부분들이 전공 과목 안에 포함되느냐의 여부일 것이다. 

굳이 전공 여부를 따지는 이유는 단 하나다. 그 사람이 아카데믹하게 데이터베이스를 공부한 적이 있는가 하는 것이다. 왜냐면 여기서 몇 가지 문제들이 파생되기 때문이다. 데이터베이스를 이론으로만 공부하게 되었을 때 발생되는 문제가 바로 너무나 추상적인 이미지의 데이터베이스로 자리잡게 되기 때문이다. 누가 데이터베이스를 만들었는지는 알고 있어도 오라클이니 SQL 서버가 무엇인지를 모른다는 것이고, 데이터베이스라고 하면 너무 어렵고 하기 힘든 부분으로만 인식되게 된다는 것이다. 즉, 이론을 현장에 바로 적용하라면 할 수가 없다는 말이기도 하다. 

아카데믹한 데이터베이스론이 잘못되었다는 이야기가 아니다. 왜냐면 이론적으로 데이터베이스를 다루고 발전시켜야 할 분야도 있기 때문이다. 하지만 실무 현장은 전쟁터와 같다. 머리만 가지고는 싸울 수 없고 전투하기 위해서는 총검술도 배워야 하고 무기도 좋아야 할 것이다. 

필자는 그러한 많은 사람들을 보았다. 물론 필자도 그 안에 포함된다. “Select * from customers”라는 명령은 알지만, 이 명령을 어디에서 어떻게 실행해야 하는지는 모른다. 

다르게 시작한 사람으로는 관계형 데이터베이스를 목적으로 해서 한게 아니라, 그 주변 지역에서 어쩌다 보니 필요해서 다루게 된 경우이다.

여기에는 두 가지 유형이 있다. 첫 번째는 프로그래머에서 시작한 유형이 있고 두 번째는 시스템 엔지니어에서 시작한 유형이 있다. 많은 DBA(Database Administrator)들이 이 두 가지 유형들에 속하며 한쪽을 잘 하면 다른 쪽은 잘하지 못하는 경우가 있다. 

프로그래머로 시작한 사람들은 쿼리 기술, 스토어드 프로시저 프로그래밍, 데이터 모델링 등에 관심을 보인다. 엔지니어로 시작한 사람들은 데이터베이스 백업, 시스템 튜닝, DBCC와 같은 시스템 관련 프로시저 명령들, 데이터 복제 등에 관심 있어 한다. 그래서 간혹 SQL 서버를 설치하고 백업 및 몇 개의 튜닝 작업들을 진행하지만 INNER JOIN 구문은 알지 못하는 사람들도 있고, 정말 기차게 쿼리를 작성하고 데이터베이스를 설계하지만 DTS Export & Import는 한번도 해보지 않은 사람도 있다. 물론, 둘을 모두 하는 사람들도 있다. 그러고 보면, DBA라는 것 안에도 여러 가지 전문분야들이 있는 것 같다. 

이러한 것들이 자신의 백그라운드가 된다. 필자는 프로그래머로 시작해서 데이터베이스 엔지니어가 된 케이스이다. 그래서 데이터 모델이니 쿼리니 ADO니 하는 이야기가 편하다. 시스템 관련 기술이나 네트워크 관련 지식은 사실 많이 부족하기 때문에 노력하지 않으면 알 수 없다. 사실 처음에는 RAID가 무엇인지 SAN이 무엇인지에 대해서도 몰랐다고 솔직하게 고백한다. 두 가지 백그라운드를 모두 가진다는 것은 너무나 어려운 일이다. 왜냐면 프로그래밍적 백그라운드는 스스로 노력해서 성취할 수 있는 부분이 많이 있다. 환경들도 시뮬레이션 해보기 쉽다. 물론, 다양한 케이스를 본다는 것은 실무적인 경험에서 나오는 것이기는 하지만 말이다. 하지만, 프로그래밍 백그라운드만으로 시스템적 백그라운드를 가지는 것은 어렵다. 

왜냐면 시스템 엔지니어로써의 케리어는 상당 부분 시스템을 다루는 현장에서 지식을 습득하고, 여기에는 그만한 규모가 되는 회사에서 시간과 노력을 기울여야 나올 수 있는 지식이기 때문이다. 그렇다면 반대의 경우는 쉽게 습득할 수 있는가? 시스템 백그라운드를 가진 사람이 프로그래밍을 쉽게 접근할 수 있을까? 사실 이것도 쉽지 않다. 아무리 책을 보고 공부하고, 학원에 다닌다고 하더라도 근본적으로 시스템 엔지니어들은 프로그램을 작성하는 사람이 아니라, 프로그램을 이용하는 사람이다. 이러한 근본적인 차이를 무시하는 것은 어렵다. 
따라서 현실적으로 자신의 백그라운드가 무엇인지를 인정하고 들어가는 것이 스트레스를 덜 받는다. 자신이 둘다 잘한다고 생각한다면 복 받았다고 생각해라. 스스로를 잘 알게 되면, 자신의 강점은 무엇이고 자신의 약점은 무엇인지가 명확해진다. 주변의 여러 DBA들을 보면 이러한 필자의 주장에 공감할 것이다. 

관계형 데이터베이스는 어떻게 공부해야 할까?

데이터베이스 전문가가 되고자 하는 많은 후배들의 경우 겪는 아픔이 어떤 것을 어떻게 공부해야 데이터베이스 전문가가 될 수 있는지를 모른다는 것이다. 그건 맞다. 왜냐면 나도 모르니까. 대부분의 이런 경우는 목표가 불분명한 경우가 많다. 도대체 어떤 데이터베이스 전문가를 원하는 것이냐. 모든 것을 다 잘하는 사람은 없다. 샘플을 만들어서 원하는 유형이 무엇인지를 찾는 것이 급선무다. 쿼리 및 애플리케이션 튜닝을 전문적으로 하는 사람이냐, 모델러냐, 아니면 유지관리 전문가냐, 아니면 데이터베이스 시스템 아키텍트냐. 아니면 전문 강사냐? 여러 가지가 있다. 하나를 골라 잡아라. 

물론, 여기에는 국내 실정도 잘 살펴 보아야 할 것이다. 우리나라에 그런 직함을 가진 전문가들이 몇이나 될까? 그리 많지 않다. 수요와 공급을 잘 판단해서 내가 가야 할 분야를 파악해야 할 것이다. 목표가 구체적이라면 내가 가야 할 단계들이 명확해진다. 그럼, 무엇을 먼저 시작할 것인가? 

어떤 사람들은 오라클을 공부해야 하나 SQL 서버를 공부해야 하나를 가지고서 고민하는 경우도 있다. 올바른 고민이다. 객관적인 입장에서 어떤 데이터베이스를 공부하느냐에 따라서 해 야할 것들이 많이 달라진다. 유닉스 환경과 윈도우 환경은 사고 구조부터가 다르니 말이다. 어떤 것을 선택해야 하느냐에 대해서는 이야기할 수 없다. 그것은 개인적인 선호의 판단이지, 기준이 있는 것은 아니다. 

다만, 어떤 데이터베이스를 공부할 때에도 중요한 것은 단편적으로 해서는 안 된다는 것이다. 처음부터 끝까지 자신의 머리로 이해가 가고 아귀가 맞물려 있어야 해당 제품을 잘 알고 있다고 할 수 있다. 필자는 항상 매트릭스 이야기를 자주 하는데, 네오가 안 것은 기능이 아니라, 기능이 왜 나와야 하는지에 대한 이해로 매트릭스를 이해했다. 

집합형 사고란....?

필자가 좋아하는 데이터베이스 용어로 집합이라는 말이 있다. 하지만 이만큼 모호한 개념으로 사람들의 머리를 아프게 하는 경우도 없을 것이다. 어떤 사람들은 모든 데이터베이스 사용자들이 집합형 사고로 전환하면 쿼리 성능이 수십 배 빨라지고 코드의 길이도 줄일 수 있는 것처럼 이야기하기도 하고, 지금 필자가 생각하는 작업 방식과 집합형 사고 방식과 아주 많은 차이가 있는 것처럼 말하기도 한다. 

사실은 필자도 잘 모르겠다. 그러한 이야기를 다루는 책이나 세미나를 들어도 도대체 집합형 사고가 무엇인지 어떻게 하라는 것인지 혼란스러웠다. 그래서 필자 나름대로 우리의 SQL 서버 환경에서 집합형 사고가 무엇인지에 대해서 진지하게 고민해보고 내린 결론은 아주 쉽고, 명쾌한 데이터베이스의 원래 개념으로 회귀하라는 것이다. 

지금 우리가 쓰고 있는 오라클이니 SQL 서버, MySQL 등은 데이터베이스라고 불리우지만, 보다 원래의 종류는 관계형 데이터베이스(Relational Database)이다. 학교에서 배웠겠지만 데이터베이스에도 네트워크니 객체 지향형이니 하는 여러 종류의 데이터베이스들이 있고 그 중에서 가장 많이 이용되는 것이 관계형 데이터베이스이다. 

관계형 데이터베이스는 Relational Database의 번역된 의미이고 우리나라에서는 모두 그렇게 해석해서 사용하고 있다. 이 데이터베이스 카테고리에서 가장 중요한 개념은 관계 즉 Relation이다. 우리나라 말에서 관계라는 것은 어떤 하나의 대상과 다른 대상간에 미치는 영향이나 교섭 등의 의미를 가진다. 자, 이렇게 되면 Relation이라는 의미는 더욱더 모호하고 추상적인 개념이 된다. 하지만 원래의 이론에서 Relation이라고 지칭되는 것은 일반적으로 물리적 모델에서 테이블(Table)이다. 그리고 일반적으로 이해되기로 Relation은 관계라는 의미보다는 집합(Set)의 의미에 더욱 가깝다. 

다시 이야기하면, Relational Database는 Set-based Database(집합 기반 데이터베이스) 혹은 Set-Oriented Database(집합 지향형 데이터베이스)라고 보는 것이 맞는다는 것이다. 이게 차라리 우리가 원어민이라면 훨씬 더 쉽게 이해할 수 있을텐데 말이다. 

이런 데에서 우리는 집합형 사고를 시작해야만 한다. 집합형 사고로 전환할 때 우리가 가장 비근한 예로 드는 것이 바로, 절차형 프로그래밍에서 객체 지향형 프로그래밍으로 전환하는 케이스를 들고 있다. 간략히 설명하면 C 언어 등에서 사용되는 절차형 프로그래밍은 매우 명쾌하다. 프로그램의 시작부터 끝까지 하나 하나의 프로세스를 밟아서 기능을 수행해 간다. 복잡한 작업의 경우에는 시작부터 끝까지 너무 코드가 길어져서 나중에 디버깅이 힘들어지니까 중간 중간에 공유 코드들을 함수(Function), 프로시저(Procedure) 혹은 라이브러리(Library)로 두어서 공유한다. 

객체 지향형은 개념을 유기체의 세계와 유사하게 하나 하나의 독립된 단위들을 두어서 독립된 단위 간에 메시지를 보내고 받아서 일을 처리하게 된다. 예를 들어서 윈도우 XP라는 메인 클래스 세상이 있다면 오피스 워드라는 하나의 다큐먼트 클래스가 프린터에게 “이 문서를 프린터 해줘”라고 부탁하면, 해당 프린터 클래스 프랜드가 이 메시지를 수락해서 업무를 처리하고, 처리 못하는 경우에는 “나 지금 바빠서 못해”라는 식으로 대꾸하는 경우이다. 그러면 우리는 각각의 클래스에 대해서 얼마나 개성 있게 만들것인지만 잡아서 프로그래밍하면 된다. 가장 중요한 것은 내가 언제 어떤 클래스와 커뮤니케이션 할 것인지만 고민하면 되는 방식으로 사고가 전환된 것이다. 

집합형 사고는 모든 처리의 단위가 집합(Set)이라는 개념에서 출발하면 된다. 상식적인 우리의 사고 방식으로 1 + 2 = 3이라는 연산은 단순한 더하기 연산이다. 여기에 더 부가할 상황 설명은 없을 것이다. 하지만 좀더 깊게 파고들면, 여러 가지 이론적 배경을 생각할 수 있다. 여기서 1이라는 것은 무엇인가? 정수이다. 2라는 것은 무엇인가? 또한 정수이다. 1 + 2라는 것은 무엇인가? 바로 정수간의 더하기라는 연산이다. 연산의 결과는 3이다. 3이라는 것은 어떻게 나왔는가? 1+2라는 것에서 나왔다. 하지만 1+2가 3이 된다는 것은 누가 정했는가? 

바로, 우리가 정하였다. 이것은 일종의 약속이며 이 약속은 정수와 정수간에만 발생된다. 예를 들어서 “1” + “2” = “12”라는 것도 있을 수 있다. 이것은 문자 간의 정해진 약속이다. 이렇게 세상에는 단일 데이터 값간에 발생되는 연산자 및 연산 결과에 대해서 미리 헌법처럼 규정해 놓았다. 데이터베이스에서는 이러한 데이터 형식 및 연산을 스칼라 타입(Scalar type) 및 스칼라 연산자(Scalar operator)라고 부른다. 

스칼라라는 의미는 벡터(Vector)의 개념과 대비되는 개념이다. 벡터는 방향과 크기를 가지고 있는 데에 비해서, 스칼라는 크기 밖에는 없다. 즉, 1차원적 개념이라는 의미이다. 집합은 여러 원소들을 가짐으로써 성립된다. 원소가 하나도 없어도 일종의 집합이다. 즉 구조만을 가진 테이블도 일종의 집합이 될 수 있고, 하나의 원소만을 가지는 집합도 가능한 것이다. 

그래서 다음의 연산은 어떻게 될까? {1} -{2} = {1}, 각각 원소 하나씩만을 가지는 {1} 집합에서 {2} 집합의 차 집합 연산(Difference operation)이다. 이것은 차감(Minus) 스칼라 연산이 아니라 집합 개념에서 정의된 차 집합 연산이다. 차 집합 연산의 의미는 {1} 집합에서 {2} 집합의 원소를 제외한 나머지 집합을 구하는 것이다. 보다 쉽게 보자면 {1,2,3} - {2} = {1,3}이 되는 것이 차 집합 연산이다. 

집합형 사고라는 것은 바로 이렇게 집합 간에 이루어지는 연산 방식에 익숙해지고, 연산 결과로써 산출되는 집합이라는 대상에 대해서 고민하는 것일 뿐이다. 무언인가 특별한 방법이 숨어있거나 시스템 성능을 확 올릴 수 있는 비전은 아닌 것이다. 

집합이라는 것은 이렇게 집합 간의 연산 방식이 다르다는 것 이외에도 몇 가지 특성들이 더 있다. 집합이라는 데이터 형식은 매우 자유롭게 표현될 수 있고, 그 크기를 더 크게 만들거나 줄여서 만드는 파생 연산이 가능하다는 것이다. 아래 여러 쿼리들에서 1번 쿼리는 원래의 소스 집합이다. 이 집합을 가공하여 2번에서는 더 작은 집합으로, 3번에서는 추가 컬럼을 가지는 집합으로 4번에서는 집합의 크기를 더블로 만들었다. 즉, 파생 집합으로 가공하는 것이다. 

1. select a, b from tables
2. select a, b from tables where a < b 
3. select a, b, c=a+b from tables 
4. select * from (select a, b from tables) as x, (select 1 union all select 2) as y 

더 찾아보면 집합의 재미있는 여러 구조들을 알 수 있을 것이다. 결론을 내자면 집합형 사고라는 것은 기존의 스칼라 연산 방식에서 탈피해서 집합과 집합간의 연산 방식으로 생각을 고쳐먹는 것이다. 그것만으로도 좀더 사고의 폭을 확장시킬 수 있을 것이다. 

필요한 비타민, 아카데믹한 것

쿼리를 잘하려면, 대략 두 가지 지식들을 공부해야만 한다. 첫 번째는 쿼리를 어떻게 결합시켜서 다양한 용도로 사용할 것인가에 관한 문제이다. 이것은 쿼리 테크닉에 관한 정보이고, 프로그래밍에서 겪게 되는 다양한 문제들을 해결시켜 준다. 두 번째는 쿼리의 내부적인 개선이다. 이것은 쿼리가 어떻게 해석되고 수행되는 지를 잘 이해하고 의도하는 대로 수행되도록 쿼리를 작성하는 것이다. 

첫 번째가 쿼리를 유용하게 하는 것이라면 두 번째는 쿼리를 올바르게 하는 것이다. 한 가지 문제점은 우리가 SQL 서버를 설계하지 않았으므로 쿼리가 어떤 기준으로 해석되고 수행되는 지를 정확하게 알 수 없다는 것이다. 당근, 마이크로소프트가 도와주면 좋겠지만 현재 그럴 의도는 없는 것 같다. 

따라서, 쿼리를 올바르게 수행하는 데에 도움을 주는 여러 지식들은 자신이 스스로 예측해야만 한다. 물론, BOL(Books Online)이나 여러 SQL 서버 전문서들에서 이러한 내용들을 다루고 있다. 그 밖에도 관계형 데이터베이스의 일반론적인 부분들도 있다. 이러한 것들은 상당부분 아카데믹한 지식이지만 SQL 서버는 그러한 지식들을 기반으로 해서 설계된 것이다. 필자 또한 마이크로소프트에서 별달리 제공되는 특별한 정보는 없다. 후훗. 언제나 그런것 좀 얻어볼까 하고 항상 고민한다. 요즘은 인터넷이 발달해서 유사한 케이스나 다른 사람의 의견 등을 참조하기가 쉬워졌지만 무엇이든지 쉽게 얻을 수 있는 답은 없다. 여러분 스스로가 노력하고 시간을 투자해야 유사한 답이라도 찾을 수 있다. 필자나 여러분들이나 밟고 있는 선은 같다. 

옵티마이저는 데이터베이스의 핵심이라고 할 수 있고 옵티마이저를 모두 이해하는 사람이 있다면, 필자도 한번 만나보고 싶다. 하지만 일반적인 정보까지는 누구나 접근할 수 있다. 예를 들어 대부분의 관계형 데이터베이스들은 유사한 방식의 옵티마징 테크닉을 사용한다. 

쿼리가 입력되면 이 쿼리가 이번에 한번 파싱된 쿼리인지를 확인하는 작업을 거친다. 물론, 여기서의 확인은 단순한 스트링 비교이다. 따라서 의미론적인 동일성은 따지지 못한다. 실행 계획을 생성해야만 하면 이 쿼리를 단순화 시키고, Cardinality, Density, Selectivity, Statistics 등의 통계 정보들을 로딩해서 플랜 트리를 생성하고 이 플랜들에 대해서 비용 분석을 따지게 된다. 그리고 병렬 계획을 고려해서 나름대로 최적의 쿼리 실행 계획이 나오게 되는 것이다. 

좀더 깊게 들어가면 로딩된 통계정보들은 어떤 기준으로 비용 판단이 되게 되는가? 쿼리의 Where 조건절과 여러 JOIN 방식들에 대한 선택도와 카디널러티 계산에 따라서 비용이 산정된다. 우리는 언제나 이러한 통계정보가 충분하다고 생각되지만 사용 가능한 통계 정보들이 없다면 쿼리 옵티마이저는 어떻게 판단할까? 이런 경우에는 기본 가정(Default Condition) 이라는 것이 사용된다. 여기에는 균등 분포(Uniform Distribution), 조건식 독립(Predicate Independence), 조인 독립(Join Independence) 등이 이용된다. 이러한 기본 조건들은 대부분의 경우 합리적인 것이지만 이 가정에서 멀리 벗어나는 데이터를 가지는 경우 쿼리는 잘못된 판단 하에 악성 실행 계획을 생성할 가능성을 가지고 있다. 

조인 메서드의 선택이라는 것도 매우 의미있는 공부이다. 조인 메소드는 크게 Nested Loop, Sort Merge, Hash Match 조인의 세가지가 있다. 상세 내용을 모두 다 설명할 수는 없다. 내용이 많으므로 간단한 비유를 통한다면 포커 하기 전에 카드를 어떻게 섞는 것이 빠른가에 대한 예시로 대신할 수 있겠다. 포커를 하기 전에 카드가 제대로 된 한 벌인가를 확인하는 방법은 여러 가지가 있을 것이다. 

우선, 카드를 스페이드, 하트, 클로버, 다이아몬드의 각 그룹으로 나누고, 한 그룹에서 하나의 카드, 예를 들어 9를 뽑은 다음에 나머지 그룹들에도 9가 들어있는 지를 하나씩 확인해 나간다면 이것은 Nested Loop이다. 각 카드를 그룹별로 일단 정리해놓는다. 그리고 모든 카드들에서 하나의 카드를 꺼내었을 때 최상단의 카드가 동일한 9번이면 이것은 Sort Merge이다. 카드를 종류가 아니라 적혀진 번호순으로 정리한다. 예를 들어서, 섞인 카드에서 하나를 꺼내어서, 첫 번째 카드가 9번이면, 이 카드를 놓는다. 다음 장이 J이면 그 옆에 놓는다. 다음 장이 9번이면 이제 9번 카드가 있는 곳에 더한다. 이렇게 하면 맨 나중에 4장의 카드가 되지 않은 그룹이 있다면 이것은 완전한 카드가 아니다. 이것은 일종의 Hash Match 조인이다. 상당히 줄여서 이야기한 것이지만 이것이 가장 쉬운 조인 메서드를 이해하는 방식일 것이다. 물론, 이외에도 어떤 경우에 어떤 조인 방식이 좋은지에 대한 논의도 있을 것이다. 

사람들은 어떤 경우 너무 눈에 보이는 부분에만 얽매이는 경우가 많이 있다. 서브쿼리 평면화(Subquery Flattening)도 마찬가지의 경우이다. 다음과 같은 쿼리가 있다고 가정해보자.

select pub_name from pubs.dbo.publishers 
where pub_id in (select pub_id from pubs.dbo.titles where type = ‘business’) 

괄호 안의 쿼리는 일종의 인라인 쿼리로써 많은 사람들의 경우 이 쿼리가 우선 수행된다고 믿는 경우가 허다하다. 혹자는 왜 SQL 서버만이 괄호 안의 쿼리를 우선해서 처리하지 않느냐고 불평하기도 한다. 하지만 그것은 사실이 아니다. 일반적으로 쿼리 안의 결과를 선행 처리해서 성능상의 이익을 볼 수 있는 경우에는 괄호 안의 쿼리의 대상이 되는 테이블이 소량인 경우이다. 즉, 이 말은 괄호 안의 쿼리를 선행 처리한다고 해서 항상 전체 쿼리의 성능이 높지는 않다라는 것이다. 오히려 괄호 안의 데이터가 많아진다면 전체적인 성능은 최악이 된다. 

이 경우 현실적인 판단은 괄호 안의 서브 쿼리와 밖의 쿼리를 조인 방식으로 처리해버리는 경우이다. 이것은 상당히 논리적인 근거가 있는 것이다. 게다가 이러한 조인 연산은 First Row & Semi Join 방식으로 연결된 첫 행 이외의 로우는 건너 뛰게 될 것이다. 이러한 것들도 올바르게 쿼리를 수행하기 위해서 필요한 것들이다. 

후기

필자의 새로운 SQL BOOK이 탄생했다. “Deep Inside T-SQL 테크닉”(영진닷컴, 540P)이다. 제목을 보면 알 수 있듯이 Deep Inside SQL Server 2000 컬럼을 열심히 읽어주시는 독자들과 수많은 SQLER들을 위한 책이다. 꼭 사지는 않더라도 서점에 가서 한번씩 책 내용이라도 보아주었으면 한다. 왜냐고? 그것은 이 오랜 컬럼을 끌어오면서 여러분들과 이야기했던 SQL 서버 프로그래밍에 대한 필자의 생각과 경험이 묻어난 것이기 때문이다. 많은 내용들이 여러분에게는 익숙한 것이고 약간의 보여주지 않았던 실무적인 예제들을 추가했다. 필자가 책을 쓰면서 나름대로 세워 논 원칙이 있는데, 그것은 바로 “내가 쓰고 싶은 만큼만 쓰자.” 라는 것이다. 언뜻, 이 말은 이해하기가 참 힘들다. 그럼, 다른 사람들은 쓰고 싶지 않은 내용도 쓴다는 것인가. 뭐냐? 하지만 사실 그렇다. 쓰고 싶지 않아도, 잘 알지 못해도, 그래도 책은 쓸 수 있는 것이니깐. 

끝으로 DEEP INSIDE SQL SERVER 2000에 관련되어 질문하고자 하시는 분이 있다면, 필자의 전자 메일 주소로 [SQLMAG]라는 말머리를 붙여서 메일을 보내기 바란다.

Posted by redkite
, |

DBMS의 통제 관리 능력을 어떻게 키울 것인가

 

데이터베이스를 안정적으로 운영·유지하기 위해서 가장 중요한 것은 ‘적절한 통제 관리 절차’이다. 이러한 통제 관리 절차의 1차 목적은 데이터베이스 운영의 안정성 확보와 보안의 유지라 할 수 있다. 이 글에서는 이러한 통제 관리 업무와 함께 데이터베이스 폐기에 이르기까지 일반적으로 수행되는 여러 가지 업무 절차 등을 살펴보자.

 

삼성SDS에서 11년간 근무하였으며, 현재는 엔코아정보컨설팅 DB사업본부의 수석컨설턴트로 4년째 근무하고 있다. 데이터베이스 전문가로서의 확고한 위상 정립과 목표 달성을 위해 항상 노력하고 있으며, 엔코아의 방법론 셋업과 데 이터모델링, DB 설계, 성능진단 및 개선 컨설팅, 개발컨설팅 등에 이르기까지 다양한 분야에서 데이터베이스 관련 부분에 대한 컨설팅 업무를 수행하고 있다.

데이터베이스를 만드는 것은 여러 전문가가 각고의 노력과 정교한 협업을 통해 만들어 내는 오케스트라 연주와 같다. 각 분야 또는 각 부분을 책임지는 전문가가 모였다고 해도 호흡이 조금만 맞지 않으면 불협화음이 튈 수 있듯이 조율되지 않은 데이터베이스는 반드시 문제를 일으키게 된다. 물론 진정한 전문가라면 완료 후에 서로 맞물리는 부분들이 자로 잰 듯이 정확하게 일치하게 된다. 바로 데이터베이스를 설계하는데 있어 사용자 요구 정의부터 시작해 전체를 몇 사람의 전문가가 나누어 진행하더라도 서로 어느 부분을 공통화시켜야 하고, 내가 어느 부분을 다른 사람에게 책임져 주어야 하는지 등이 오랜 훈련과 경험을 통해 일치하기 때문에 큰 시스템도 분할 작업에 의해 정복이 가능하다는 것이다.


아이를 어떻게 키울 것인가?

대부분의 SI 프로젝트는 사용자 요구부터 시작해 시스템의 근간이 되는 데이터 모델이 완성되고, 이 골격 위에 애플리케이션이라는 잘 만들어진 옷이 입혀지면 비로소 보기 좋은 시스템이 완성된다. 이 과정 역시 데이터베이스 전문가 외에도 애플리케이션 전문가, 아키텍처 또는 시스템 통합 전문가, 하드웨어 및 네트워크 전문가 등 많은 관련 전문가가 참여하게 되고, 이들이 화음을 맞추지 않으면 결과적으로 듣기 싫은 음악처럼 사용자에게 외면당하는 시스템으로 전락하고 프로젝트는 실패로 끝나게 된다.

이처럼 어렵고 힘든 과정을 거쳐 사용자가 원하는 시스템이 마무리되면 사실상 개발팀의 임무는 완수되었다고 할 수 있다. 그 때부터 유지보수팀 또는 사용자측의 대리인 격인 관련 IT 부서가 시스템의 운영과 유지보수를 책임지게 되며, 이들에 의해 운영 시스템은 또 다른 인생 항로를 가게 된다.

사람도 낳아준 부모와 기르는 부모가 다를 수 있듯이 시스템도 한 조직이 개발에서 운영까지 책임지는가 하면 만든 사람과 운영하는 사람이 다른 경우도 허다하다. 또한 사람도 수태 기간을 거쳐 세상에 나올 때까지 많은 공을 들여야 하지만, 정작 성장 과정에서 소홀하면 결국 엉뚱한 운명으로 치닫게 되는 것처럼 아무리 잘 만들어진 시스템이라 하더라도 결국 운영을 어떻게 하느냐에 따라서 시스템이 발전하기도 하고 쇠락의 길을 걷다 조기 폐기의 운명을 맞이하기도 한다.

지금까지 많은 서적과 사람들의 관심은 대부분이 시스템을 어떻게 하면 잘 만드느냐에 맞춰져 왔으며, 사실상 서점에 나가 보면 시스템 운영을 어떻게 해야 하는지 데이터베이스를 어떻게 관리해야 하는지 등에 대해서는 관련 서적을 찾아보기가 매우 어렵다. 시스템 운영, 그 중에서도 특히 데이터베이스의 운영은 체계적인 전문 지식과 고도의 훈련 없이는 수행하기 어려운 업무이다 보니 이와 같은 업무를 가이드하거나 지침을 삼을만한 내용을 담고 있는 대중 매체가 거의 전무하다. 게다가 대부분의 조직에서 선배로부터 후배에게로 일부에 한해서만 이어져 내려가다 보니 많은 사람들이 데이터베이스 운영에 대해 무지하거나 전체를 알기 못하는 것이 현 사정이다. 데이터베이스 운영을 전담하는 전문가를 데이터베이스 관리자(이하 DBA)라 부르는데, 앞서 얘기한 것처럼 이들의 업무 영역이 보편화되어 있거나 관련된 사람들에게 충분히 이해가 되지 않아 자신을 알아주는 사람만 만나면 끝도 없이 속내를 털어놓는 사람들을 필자는 많이 보아 왔다.

데이터베이스는 치밀하고 구체적이면서 또한 입체적인 설계 과정을 거쳐 탄생하게 되며 정교하게 짜 맞춰진 애플리케이션에 의해 생명력을 갖게 된다. 이렇게 탄생한 데이터베이스는 애플리케이션에 의해 생성되고 사용되며 소멸되는 시험 과정을 수차례 반복하면서 저장되는 데이터들의 무결성을 보장받게 되고, 데이터를 액세스하는 애플리케이션의 응답속도 및 서비스 품질이 사용자 요구 수준에 만족할 만한 상태에 이르기까지 안정화 과정을 거치게 되며, 그 이후에 적절한 운영 및 통제 절차에 의해 개인 또는 기업의 데이터가 유지·관리되고, 그 필요성이 다하는 시점에서 적절한 폐기 절차에 의해 생명을 다하게 된다.

통제 관리를 하는 1차적인 목적은 데이터의 안정성 확보와 보안의 유지라 할 수 있다. ‘안정성’이라는 의미에는 ‘서비스가 중단되지 않도록 한다’는 것 외에 ‘최상의 성능을 유지한다’는 의미가 포함되어 있는데, 많은 사이트에서 데이터베이스가 가사 상태에 빠져 있거나 정신을 못 차리고 시름시름 앓고 있는 것도 따지고 보면 서비스의 지속에만 의미를 두고 운영해 온 결과라 해도 과언이 아니다.

필자는 이 글을 통해 많은 사람들이 궁금해 하는 데이터베이스 운영에 대해 데이터베이스의 탄생 이후부터 성장 변화를 거쳐 노화하고 사멸(폐기)에 이르기까지 수행되는 관련 업무 범위와 내용에 대해 소개함으로써 데이터베이스 운영에 대해 알고자 하거나 또는 이러한 업무를 하고자 희망하는 이들의 궁금증 해소를 돕고 관련 실무를 담당하는 이들에게도 혹시 놓치고 있는 업무가 있는 지를 살펴보는 계기를 제공하고자 한다. 그럼 탄생된 데이터베이스를 DBA가 어떻게 잘 키우고 돌보는지 살펴보도록 하자.


데이터베이스 운영을 위해 필요한 요소

어떤 대상을 ‘관리’하고자 한다면 우선 ‘관리할 대상’이 명확해야 하며, 거기에 ‘그 일을 할 사람’과 ‘일하는 방법(절차)’ 등이 필요하다. 이런 관점에서 데이터베이스를 관리할 때 필요한 사항을 꼽아 본다면, ‘관리할 대상’은 당연히 데이터베이스가 될 것이며(좀더 명확하게는 최종 사용자에게 서비스가 되고 있는 운영 데이터가 저장된 운영 데이터베이스라 할 수 있다), ‘일을 할 사람’은 DBA 또는 운영조직이라 할 수 있으며, 마지막으로 ‘일하는 방법(절차)’는 바로 이 글의 주요 논제가 될 데이터베이스 운영에 대한 제반 업무 절차가 될 것이다.

데이터베이스는 개인 또는 기업, 단체, 공공기관 등 누구나 필요에 의해 만들고 운영할 수 있는 것이며, 데이터베이스 구성 매체로 오늘날 가장 보편화되어 있는 것은 관계형 데이터베이스 관리 시스템(이하 RDBMS)이지만, 그 외에도 다양한 형태의 데이터베이스가 존재할 수 있고, 여기에서 이들 모두를 거론하는 것은 논제에서 다소 벗어나기 때문에 이 글에서는 논외로 하고, RDBMS를 운영하고 있는 기업을 모델로 하여 주로 기업에서 데이터베이스를 어떻게 관리하고 있는가에 초점을 맞추어 설명하고자 한다.


DB 운영을 위한 환경

데이터베이스는 1차로 디스크 상에 저장이 된다. 시스템 규모와 환경에 따라 RAID(Redundant Array of Inexpensive/Independent Disks)와 같은 별도의 저장장치에 저장해 운영할 수도 있고, 작은 규모에서는 서버 자체의 로컬 디스크 상에 데이터를 저장할 수도 있다. 그러나 디스크 내에 무한정 데이터를 보관할 수는 없기 때문에 대부분의 기업 전산실에서는 저렴하면서도 장기간 보관이 가능한 보조 저장매체를 채택해 데이터의 백업본을 분리·저장하기도 한다. 로컬 디스크나 RAID와 같은 첫 번째 데이터 저장장치는 대개 항온 항습 환경이 갖춰진 별도의 시설을 사용하게 되고, 필요에 따라 분리된 장소에서 유사시를 대비해 동일 데이터의 복사본을 저장하기도 한다.

이와 같은 복제 데이터 보관은 대개 각종 재난, 재해로부터 기업의 데이터를 안전하게 보호하고 핵심 업무의 연속성을 유지하기 위한 사전 준비 행위이며, 이에 대한 업무는 BCP/DRP(Business Continu ity Plan/Disaster Recovery Plan)이라는 분야로서 9.11테러 이후 전세계의 많은 기업들의 이슈가 되고 있다. 백업 데이터의 보관 역시 BCP/DRP와 무관하지 않다.

데이터베이스 운영이라는 관점에서 보면 지금까지 얘기한 데이터 보관 문제, 즉 데이터를 어디에 어떻게 보관하는가 하는 문제가 데이터베이스 운영과 밀접한 관련이 있긴 하지만 대부분의 기업에서는 시스템 관리 조직이나 정보전략 부서 또는 정보보호 담당 부서 등에서 취급하기 때문에 이 부분에 대한 상세한 설명은 생략하고 직접적인 데이터베이스 운영 업무와 연관된 부분에 대해 설명하고자 한다. 데이터베이스의 운영 환경과 관련해서 반드시 짚고 넘어갈 부분이 있다면 시스템의 개발과 운영이 가급적 별도로 분리된 서버 상에서 이루어져야만 한다는 것을 들고 싶다.

“전쟁터에서 사격 연습을 하지 말라”

분리된 운영 환경을 구성해야 하는 이유는 시스템 개발시의 개발 환경(또는 개발 서버)과 테스트를 위한 테스트 환경(또는 테스트 서버), 실 사용자가 액세스하는 운영 환경(또는 운영 서버)으로 구분해 각각의 환경에 분리된 애플리케이션과 데이터베이스를 유지함으로써 시스템 개발 및 유지보수 업무 과정에서 원본 또는 운영 데이터가 훼손되지 않도록 하기 위함이다.

기업의 IT 관련 부서에서 근무한 경험이 있는 사람이라면 한번쯤은 “전쟁터에서 사격연습을 하지 말라”는 말을 들어 봤을 것이다. 필자 역시 전산실 근무 시절 귀에 못이 박히도록 들어 온 말이다. 막상 전쟁터에 가서 그때서야 사격연습을 한다면 어떻게 쳐들어오는 적을 물리칠 수 있느냐는 의미로, 실제 서버에서 운영되는 데이터를 가지고 이런 저런 테스트를 하는 것은 매우 위험천만한 일이라는 점을 강조해 빗댄 말이다.

<그림 1> 다양한 개발/테스트/운영환경 구성 사례

대부분의 사람들은 과연 누가 실 운영 서버에서 데이터처리 연습을 하겠느냐고 반문할 수 있지만, 필자의 경험에 비춰보면 의외로 이런 사람 내지 이런 IT 조직이 적지 않다는 것이다. 그래서 운영 서버에서 함부로 데이터 처리 테스트를 해보거나 테이블 또는 데이터파일 처리 테스트를 해보다가 뜻하지 않게 중요한 데이터 또는 테이블, 데이터 파일 등을 없애버리게 되어 거의 사색이 된 얼굴로 어쩔 줄을 몰라 하던 개발/유지보수 담당자들의 모습을 여러 번 보아 왔다. 이런 경우 대개는 백업 데이터를 이용해서 무사히 복구하기도 하지만 어떤 경우에는 백업 데이터가 없거나 백업 데이터가 있더라도 삭제 순간의 데이터와 많은 차이가 있어 삭제되기 바로 이전의 시점으로 되돌리지 못해 담당자가 시말서를 쓰거나 징계를 받는 모습도 간혹 보았다.

이와 같은 어처구니가 없는 일이 왜 일어날 수 있는가를 곰곰이 생각해 보면, 그 속에는 데이터에 대한 경시 사고가 있음을 알게 된다. “그까짓 데이터? 다시 입력하지, 뭐!” 혹시 여러 분들은 이런 말을 쉽게 내 뱉어 본 적은 없는가? 이런 말을 쉽게 하거나 쉽게 생각하는 사람이 있다면 데이터에 대한 자신의 생각을 깊이 반성해 볼 필요가 있다. 기업의 데이터 하나하나는 모두 기업 활동에서 얻어지는 소중한 자산이며, 이런 데이터들을 잘 모으고 가공함으로써 그야말로 백만불짜리 정보를 만들어 낼 수 있게 된다. 데이터베이스 입장에서 생각해 보아도 자신이 저장하고 있는 데이터가 있어도 그만, 없어도 그만인 데이터들이라면 얼마나 서글프겠는가? 여러분들도 자신의 존재 가치를 인정받지 못한다고 생각해 보라. 말 못하는 데이터베이스라고 해서 함부로 취급한다면 그 결과는 결국 사람에게 되돌아 간다. ‘Garbage-In-Garbage-Out’이라고 하지 않았는가?

결론적으로 말하면 사용자의 데이터는 별도로 분리된 환경에서 적절하게 보호되어야 한다는 것이며, 개발/유지보수 담당자는 운영환경과 물리적으로 분리된 개발/테스트 환경에서 실제로 사용되는 제대로 된 데이터를 가지고 개발 및 테스트를 하면서도 운영 환경(서버)의 원본 데이터가 훼손되지 않도록 잘 통제하고, 개발/테스트/유지보수 등의 행위로 인한 서버 부하로부터 사용자들이 실제 데이터에 접근하는 데 영향을 받지 않게 보호하며, 운영환경의 데이터베이스에 대한 변경에 대해 적절한 형상관리를 함으로써 사용자의 데이터가 항상 최적의 성능을 유지하면서 안전하게 보호되어야 한다는 것이다.

이러한 문제를 잘 통제하기 위해 다양한 업무 기준과 절차가 필요하게 되고, 이러한 기준과 절차가 얼마나 실현 가능하게 만들어져 있으며 각 관련자들이 이를 얼마나 성실하게 준수하느냐가 안정적인 데이터베이스 운영에 있어서 최대 관건이라고 할 수 있다. 만일 여러분이 아주 고가의 귀중품을 가지고 있다고 가정하면, 그런 물건에 아무나 함부로 접근하게 하겠는가? 아마도 모처에 잘 보관해 놓고 신분이 확실하고 믿음이 가면서 자신의 통제에 잘 따르는 사람으로만 한정하여 접근을 허용할 것이고, 거기에 자신도 분명히 함께 있으려 할 것이다. 개인의 귀중품에도 이와 같은 철저한 보호 통제 절차를 적용하는데, 하물며 기업의 활동 내역이 담긴 데이터베이스는 오죽 하겠는가? 그러면 지금부터 데이터베이스 운영과 관련된 여러 가지 업무 영역에 대해 살펴보기로 하자.

데이터베이스 운영 업무

데이터베이스의 관리 방법 및 절차 등에 대해서는 데이터베이스를 운영하는 IT 부서 또는 관련 조직마다 각자의 환경에 적합한 지침을 만들어 적용하고 있다. 전체적인 전산 환경을 관리하기 위한 활동 측면에서 보면 좀더 많은 업무 영역이 있을 수 있으나, 범위를 데이터베이스 관리와 운영으로 국한한다면 다음과 같은 업무 영역을 들 수 있다. 이는 제반 여건 및 환경에 따라 업무 범위에 가감이 있을 수 있다. 중요한 것은 각자의 처한 환경에 맞게 적절한 관리 영역을 설정하는 것이다. 너무 넓거나 반대로 너무 좁게 업무 영역을 설정하게 되면 무리가 따라 손이 미치지 못하거나 관심 밖의 일이 되어서 관리와 통제가 제대로 이루어지지 않을 수 있다.

데이터베이스가 항상 최적의 상태로 유지될 수 있도록 관리하기 위한 업무 범위에는 어떤 것들이 있을까? 그에 대해서는 의견이 분분하겠으나 대략적으로 다음과 같이 요약해 볼 수 있다.

◆ 데이터베이스 이관 관리
◆ 데이터베이스 전환 관리
◆ 용량계획
◆ 백업 및 복구
◆ 운영 통제(변경 관리)
◆ 성능관리 및 튜닝
◆ 장애 대책 및 위험 관리
◆ 운영현황 보고

이 글에서 알아볼 운영의 핵심 사항으로, 이들이 각각 어떠한 특징이 있고 어떤 점이 중요한 지에 대해 알아보자.

데이터베이스 이관 관리

데이터베이스 이관 관리는 전산 설비 이주시나 서버를 교체하는 등 데이터베이스의 보관 장소가 변경됨으로써 발생되는 서비스 중단을 최소화하기 위한 목적으로 수행된다. 살고 있던 집을 떠나 새로운 집으로 이사를 갈 때도 이삿짐센터 선정부터 많은 것들을 계획·결정해야 하고, 이것을 잘 해야 이사가 무사하게 잘 끝나게 되는 것처럼, 데이터베이스를 이사시킬 때도 철저한 사전 준비가 있어야만 무사히 이사를 마칠 수 있다. 더구나 집을 이사하는 문제는 개인에 국한하겠지만 데이터베이스를 이사시키는 것은 수많은 사용자들과 관련이 되기 때문에, 여기서 발생하는 문제는 곧바로 엄청난 손실로 이어질 수 있는 가능성을 안고 있다. 이 때문에 진행 절차에 대한 계획을 수립하고 예상되는 제반 문제요소를 도출해 사전에 대비책을 강구하는 한편 수립된 계획에 따라 사전에 모의 훈련을 실시해 이관에 따른 불확실한 요소를 제거해 내는 것들을 주요 골자로 하는 계획이 필요한 것이다.

이러한 상황은 데이터베이스 이관을 데이터베이스 관리 업무의 범주로 논할 때 혹자는 직접적인 데이터베이스 관리 업무라고 보기에는 다소 무리가 따른다고 할 수 있겠으나, 데이터베이스 운영의 목표가 안정된 서비스가 이루어지도록 하는 것이라면 데이터베이스에 대한 서비스 관리 관점에서 중요한 업무 요소라 할 수 있다.

이를 위해 DBA는 물론이고 시스템 관리자 등 관리 인원은 평상시 장애복구 및 최단 시간 서비스 재개를 위한 교육과 훈련이 철저히 준비되어 있어야 하고, 이관을 실행하기에 앞서 철저한 계획수립과 검토, 도상 훈련, 체크리스트 등을 통해 실행시 관련자들이 우왕좌왕하지 않고 일사분란하게 움직여 목표한 시간 내에 이관이 완료되도록 해야 한다. 특히 서버 교체의 경우 RDBMS를 사용하고 있다면 이관 후에 옵티마이저라고 하는 질의 처리기가 기존과 다른 실행계획을 수립하게 되어 시스템 성능이 저하되거나 심지어 데이터 오류를 발생시킬 가능성도 있기 때문에 DBA는 이관 후에 이러한 상황이 발생하는지 꼼꼼하게 따져 보아야 한다. 해당하는 RDBMS에서 제공되는 SQL 트레이스 유틸리티(SQL 서버의 경우에는 프로파일러)를 이용하면 이러한 변화를 용이하게 발견할 수 있다. 이관 후 일정 시간 동안 트레이스 자료를 수집해 분석해 보면 응답시간이 많이 걸리거나 실행계획 변경으로 I/O에 병목현상이 발생하고 있는 질의문(SQL) 등을 쉽게 찾아 낼 수 있기 때문이다. 이관 전과 후에 각각 트레이스 자료를 수집해서 비교해 보면 차이가 쉽게 발견된다.

데이터베이스 전환 관리

데이터베이스 전환은 대체로 다음과 같은 두 가지 상황에 의해 발생한다.

  1. 새로 개발된 시스템을 운영환경에 적용시 기존 데이터의 전환
  2. DBMS 교체나 업그레이드에 따른 기존 데이터 전환

데이터베이스 전환시 앞의 두 가지 경우의 포인트는 조금씩 차이가 있다. 첫 번째 경우인 신규 개발 시스템에 기존 데이터를 전환할 때는 전환 전후에 데이터가 정확히 일치하는지의 여부가 가장 중요하다. 물론 전환 과정에서 데이터 클린징에 의해 일부 제거되는 데이터가 존재할 수 있으나 전환 대상과 클린징이 되는 데이터를 모두 합했을 때 기존 데이터와 총 건수가 일치해야 한다. 즉, 사전에 정의한 검증 요소와 기준 값에 정확히 일치해 전환 과정에서 누락되거나 변질된 데이터가 없음을 입증하는 것이 중요하다.

<그림 2> 관계형 데이터베이스 사용환경에서 AS-IS 데이터를 TO-BE 데이터로 전환하는 절차 사례

데이터의 전환은 대개 별도의 프로그램을 작성해 수행하고 있으나 관계형 데이터베이스 시스템이라면 가급적 SQL 중심으로 처리할 것을 권하고 싶다. 복잡한 맵핑 규칙을 이유로 C나 COBOL과 같은 3세대 언어 처리방식으로 작성한 프로그램은 대부분 복잡한 IF...THEN...ELSE 처리와 수많은 반복구조(루프)를 갖고 있기 때문에, 전환 대상 데이터량이 클수록 자칫 전환에 소요되는 시간이 장기화되어 기껏 개발한 신규 시스템의 오픈 시점에 영향을 줄 가능성이 크다. 그러나 SQL 중심의 전환 처리에서는 관계형 데이터베이스의 옵티마이저가 SQL을 대상으로 최적화를 수행하기 때문에 훨씬 향상된 성능을 얻을 수 있으며, 조인 방식이나 조인 순서 등을 바꾸거나 간단한 SQL 재구성만으로도 수행속도가 크게 달라질 수 있다. 여기에 더해서 SQL 수행시 병렬처리나 세션 메모리 조절 등의 방법을 적용하면 더욱 빠른 SQL 처리 성능을 얻을 수 있기 때문에 이와 같은 SQL 중심의 전환처리 방식을 적용해야만 목표한 시간 내에 전환을 완료하고 무사히 시스템을 오픈할 수 있게 된다.

요즘과 같이 억 단위의 대용량 데이터를 저장하고 있는 테이블의 존재가 보편화되어 가는 추세에서는 더욱 이런 처리 방식이 중요하다. 물론 이러한 모든 전환 과정이 DBA의 책임은 아닐 것이나 최소한 데이터베이스 관리 차원에서 전환이 순조롭게 목표한 시간 내에 이루어질 수 있는지의 여부를 판단하고 전체적인 데이터베이스 관리 업무의 범주 내에 반영하려면 상당한 수준의 관계형 데이터베이스 원리 이해와 SQL 실력을 갖추고 있어야만 가능하다.

두 번째 경우인 DBMS 교체는 첫 번째 경우와 크게 다르지 않으나 업그레이드시에는 전환 전후의 데이터에 변경이 없음을 검증하는 것에 더해서 업그레이드 대상 버전에 버그는 없는지 혹은 이로 인해 서비스가 중단될 가능성이 있거나 데이터 자체에 오류가 발생할 가능성은 없는지 등을 꼼꼼하게 따져 보아야 한다(해당 DBMS의 사용자 그룹이나 포럼 사이트 등을 방문해서 알려진 버그가 없는지 등을 살펴보는 것도 좋은 방법이다). 특히 RDBMS의 경우 버전 업그레이드에 따라서 옵티마이저라고 하는 질의 처리기가 기존과 다른 실행계획을 수립하게 되어 시스템 성능이 향상될 수도 있지만 반대로 성능이 저하되거나 심지어 데이터 오류를 발생시킬 가능성도 있기 때문이다.

예를 들어 기존 환경에서 프로그램A에 내장된 SQL이 테이블 A의 1번 인덱스에 접근해 관리번호를 생성한다고 가정했을 때, DBMS 업그레이드 이후 옵티마이저가 수립하는 실행계획이 바뀌어 다른 인덱스를 사용하도록 되었다면 이때 이 인덱스로 접근해 생성하는 관리번호는 이미 발생한 번호가 중복 발생되거나 하여 오류 상황으로 귀결될 가능성도 있을 수 있다는 것이다.

이와 같은 옵티마이저 실행계획과 성능의 변화에 대한 검증은 데이터베이스 이관에서 설명한 것처럼 SQL 트레이스 유틸리티를 이용하면 변화를 쉽게 발견할 수 있으며, 추가적으로 sar나 iostat과 같은 명령을 이용해 시스템 부하를 측정해 보는 것도 좋은 방법이다.

데이터베이스 전환은 전환 과정 자체를 치밀하게 계획하고 통제함으로써 실행 과정에서의 오류 가능성을 제거하고 목표한 시간 내에 전환을 완료하는 것도 중요하지만, 전환 결과 데이터에 대한 신뢰성 확보도 그에 못지않게 중요하다. 전환 데이터에 대한 검증시 많이 사용하는 방법으로 대략 4가지 정도를 들 수 있는데, 이들에 대한 전환 전후의 비교를 통해 전환시 오류가 있었는지 여부를 검증할 수 있다.

◆ Financial Total 또는 Monetary Total : 컬럼 중 급여처럼 재무적으로 의미가 있는 컬럼에 대한 합계
◆ Hash Total : 일련번호처럼 재무적으로 무의미한 컬럼의 합계
◆ Total Items : 수량과 같이 양적인 의미를 가진 컬럼의 합계
◆ Total Document : 로우(rows) 또는 레코드(records)에 대한 총 건수

용량계획

용량계획은 최소한의 비용으로 최상의 서비스를 보장할 수 있는 범위 내에서 이루어지는 서버, 디스크, CPU, 메모리, 네트워크 구성 등에 대한 적정량을 결정하고 유지하기 위한 업무라고 할 수 있다. 특히 데이터베이스 운영과 관련해 적정한 디스크 용량을 유지하는 것은 안정적인 데이터베이스 서비스를 위해 매우 중요한 사항이다. 사람도 적당한 공간이 있어야 숨도 쉬고 활동하며 살아가는 것처럼, 데이터에게도 적절한 활동 공간은 데이터가 항상 최상의 상태로 서비스될 수 있기 위한 기본 조건이라 할 수 있다.

데이터베이스를 액세스할 때 병목이 발생하지 않도록 하기 위해서는 적절한 I/O 분산이 필수며, 이를 위해서는 물리적으로 구별되는 하나 이상의 디스크를 할당할 수 있어야 한다. 이러한 문제를 해결하기 위해 여러 개로 구성된 로컬 디스크를 사용하거나 RAID와 같은 디스크어레이를 사용하는 것이 일반적이다. 여러 개로 구성된 디스크를 사용하는 경우 디스크 공간이 충분하다면 좀더 빠른 액세스 속도를 구현하기 위해 스트라이핑(striping)과 같은 기법을 사용하기도 한다. 이와 같이 최적의 데이터베이스 서비스를 유지하기 위해서는 적정한 양의 디스크가 필요한데, 이는 곧 비용으로 귀결되기 때문에 데이터 증가 추이를 잘 분석해 사전에 철저한 대비와 검토를 통해 장기적인 계획에 의해 이루어지지 않으면 기업의 입장에서는 많은 비용 낭비는 물론이고 서비스 품질에도 영향을 받을 수 있다. 이는 사람이 성장 과정에 맞추어 적당한 시점에서 좀 더 크고 편안한 옷으로 갈아입는 것과 같은 이치이다.

데이터의 증가 추이 예측은 이 글에서 소개하는 데이터베이스 운영 업무 중 ‘운영현황 보고’와도 연계되는데, 매월 운영현황 정보를 수집하고 분석해 월별로 데이터가 증가하는 추이를 분석해 보면 어느 시점에 가서 디스크 용량이 부족하게 되는지 예측이 가능해진다. 이런 예측을 통해 저장공간 부족에 의해 시스템 가동이 중단되지 않도록 시기적절하게 디스크를 추가 또는 교체해 데이터베이스가 항상 최상의 상태로 서비스될 수 있도록 해야 한다. 운영현황 분석과 함께 추가로 고려할 것은(운영현황 분석에 의한 예측은 어디까지나 산술적인 계산에 의한 것이므로) 이벤트나 캠페인 실시, 업무범위/량 증가, 트랜잭션 증가 등으로 업무 환경 변화에 의해 증가 폭이 달라질 수 있음을 생각해야 한다. 이 때문에 대개는 불완전하더라도 증가 추이를 예측할 때 보정계수를 사용한다. 보정계수는 아이들 옷을 살 때 몸이 계속 자라나는 것을 고려해서 현재보다 한두 치수 더 큰 것으로 사는 것과 같다. 보정계수는 앞에서 기술한 여러 가지 업무 변화 가능성 등을 고려해 각자에 맞게 설정해 사용하면 된다.

백업 및 복구

백업은 두 가지 목적으로 운영된다. 하나는 유사시 신속하게 최신의 상태로 데이터베이스를 되돌리기 위함이고, 또 하나는 한정된 저장 공간의 제약사항을 보완하기 위해 마그네틱 테이프와 같은 보조 저장장치를 이용해 과거에 발생되어 현재는 거의 사용이 되지 않는 오래된 데이터나 중요 데이터에 대한 복사본을 저장하는 것이다. 어떤 DBMS를 사용하고 있느냐에 따라 다소간의 차이가 있을 수는 있으나 대개의 경우 첫 번째 목적을 위해 아카이브와 같은 1차 백업본을 만들기도 한다. 하지만 이 역시도 데이터 발생 및 변경 등이 활발하면 한정된 디스크 공간 내에 많은 아카이브 정보를 보관해야 하므로 비용 상의 문제가 발생해 대개의 경우는 어느 정도의 아카이브 자료가 쌓이면 보조 저장매체로 이동시켜 보관한다.

보조저장 매체로 가장 널리 사용되는 것은 광 디스크와 마그네틱 테이프인데, 저렴한 가격으로 많은 정보를 저장할 수 있고 재사용성 측면에서도 우수한 마그네틱 테이프가 좀더 보편적으로 사용되고 있다. 마그네틱 테이프를 사용할 때는 반드시 사용기한과 기록횟수를 확인해야 하며, 사용기한이 경과한 오래된 테이프를 사용하거나 보장된 기록횟수를 넘긴 테이프를 사용함으로 해서 막상 필요할 때 저장된 정보를 제대로 읽지 못하게 되어 낭패를 당하지 않도록 주의해야 한다.

백업은 대개 변경된 부분만 따로 읽어 들여 만들거나 혹은 전체 복사본을 만드는 방법으로 진행되는데, 변경된 부분만 저장하면 백업 데이터 생성이 짧은 시간 내에 이루어질 수 있어서 대용량 데이터를 저장하고 있는 환경에서 백업 생성시 유리한 장점이 있다. 반면에 백업된 내용 자체가 그날그날 또는 특정 시점에서의 변경 부분만 갖고 있으므로 그 기반이 되는 데이터 전체가 문제시 되는 경우에는 이것만 가지고는 제대로 복구할 수 없다는 단점이 있다. 전체를 그대로 복사본으로 만드는 방법은 전체 데이터에 문제가 생겼을 경우에도 그대로 다시 복사해 넣으면 전체 데이터를 복구할 수 있다는 장점이 있는 반면에, 대용량 데이터를 저장하고 있는 환경에서는 백업 생성시 매우 많은 시간이 소요되어 자주 백업본을 생성하기가 곤란하고, 이 때문에 ‘최신’과는 항상 괴리가 있다는 단점이 있다. 그렇기 때문에 전체 백업은 불연속적인 시점에서의 스냅 샷만 가질 수밖에 없고, 이것은 곧 데이터 복구시에 전체 백업본만 가지고 있는 경우에는 문제가 발생하기 바로 전 상태로 원상 복구시키기가 불가능하고, 최종적으로 전체 백업을 받은 시점까지만 복구가 가능하게 된다.

이러한 문제를 극복하기 위해 대부분의 시스템 관리자나 DBA는 주기적으로 전체 백업을 수행하고 매일 또는 수시로 변경 부분에 대한 백업을 수행해 보관하고 있다. 그리고 이를 통해 전체 데이터에 대한 장애 발생시 일단 최종적인 전체 백업본을 먼저 복사한 후 그 위에 시점 별로 생성한 변경 사항에 대한 백업을 시간 순으로 다시 반영해 나감으로써 장애가 발생하기 바로 전 시점으로 완벽하게 복구가 가능하게 된다.

[ 디스크 스트립핑 ] 

복수의 개별 디스크에 하나의 데이터(테이블, 데이터 파일 등)을 물리적으로 분산 저장되게 하고, 논리적으로 하나의 오브젝트처럼 사용함으로써 데이터 엑세스시 동시성 향상에 의해 엑세스 성능을 향상시키는 물리적인 저장 기법이다. 관계형 데이터베이스에서 하나의 데이터 파일을 여러 개의 디스크에 할당함으로써 저장 데이터가 할당된 복수 개의 디스크에 나뉘어 저장되도록 하거나, 디스크 구성시 가상적 스트라이프(virtual stripe)를 작성하여 이들 디스크를 컴퓨터의 운영 체계가 단일의 디스크 구동 장치(disk drive)로 인식하도록 하여, 이들 디스크상에 존재하는 똑같은 크기의 디스크 분할(disk partition)의 집합을 단일 디스크 볼륨(disk volume)으로 종합하여 사용하기도 한다. 이렇게 하면 같은 볼륨 내에서의 다중 입출력 동작이 동시에 진행될 수 있게 되어 성능이 크게 향상된다.

운영 통제

운영 통제는 데이터베이스를 운영하는 과정에서 애플리케이션 개발/유지보수 담당자들이 운영환경에 반영된 데이터베이스의 형상을 변경하고자 하는 경우에 대한 적절한 통제 절차를 의미하며, 주로 신규로 파일, 테이블, 인덱스 등을 생성하거나 이들에 대한 변경, 삭제, 데이터 업로드(임포트)/다운로드(익스포트) 등이 해당된다. 또한 좀더 확장해서 생각해보면 잘못 변경된 애플리케이션에 의해 저장 데이터가 예기치 않게 삭제되거나 잘못된 값으로 바뀌지 않도록 애플리케이션에 대한 소스코드 검사나 SQL 검증까지도 생각해 볼 수 있다. 물론 값이 틀려지지 않았더라도 시스템 성능에 문제가 발생하지 않도록 하기 위해서도 변경된 애플리케이션의 소스코드나 SQL 검사는 반드시 필요하다. 운영 통제는 데이터베이스 운영과 관련한 업무 중에서 가장 중요하고 빈번하게 수행되는 핵심 업무라 할 수 있으며, 아이들을 갖가지 외부 위협이나 영향으로부터 적절하게 보호하지 않으면 아이들의 성장에 문제가 생길 수 있는 것처럼 데이터베이스 또한 계속되는 변경이나 비인가된 접근으로부터 적절하게 보호되지 않으면 안정성에 치명적인 결함이 발생할 수 있다.

이러한 업무와 관련해 애플리케이션 개발자들에 대해 DBA가 적절한 통제 수단이나 권한을 가지지 못한다면 데이터베이스를 최상의 상태로 유지하는 데 있어서 어려움을 겪게 된다. 이 때문에 많은 데이터베이스 운영자들은 때때로 애플리케이션 개발/유지보수 담당자들과 데이터베이스 관리 목적을 위해 충돌이 발생하기도 하며, 이때 데이터베이스 운영자의 직급 및 기술 수준, IT 부서 또는 관련 조직 내에서의 위상 등에 따라 데이터베이스의 안정적인 운영 문제는 많은 영향을 받게 된다.

필자도 과거 개발자 시절에 DBA로부터 변경 신청한 애플리케이션의 SQL 성능 문제로 호되게 혼난 경험이 몇 차례 있는데, 그 DBA 덕분에 당시 필자가 근무하고 있던 곳의 전산 시스템은 여러 곳으로부터 벤치마킹 대상이 되며 인기가 있었던 기억이 있다.

운영 통제는 데이터베이스를 무분별한 변경 또는 액세스로부터 보호하기 위한 보안 개념에서 출발하며, 시스템 유지보수의 목적을 대개 시스템의 가용성 향상과 사용자의 요구사항 변화 반영, 시스템의 생명 연장으로 보았을 때 이의 연장선에서 이러한 변화로부터 데이터베이스의 안정성, 가용성, 신뢰성을 확보한다는 측면에서 매우 중요한 업무이다.

운영 통제와 관련해 대개 애플리케이션 개발/유지보수 담당자들과 DBA의 관계는 상호 협력적이기보다 다소 과격한 표현일 수 있으나 ‘창과 방패’ 관계에 가깝다. 애플리케이션 담당자 입장에서는 사용자 요구사항에 의해 개발/변경된 내용을 용이하고 신속하게 운영환경에 반영하고자 하는 습성을 보이고, DBA 입장에서는 애플리케이션 담당자들의 작업 결과를 무턱대고 반영했을 때 성능저하나 예기치 못한 오류 상황으로 이어질 수 있기 때문에 가급적 문제가 없다는 확신을 가지고 형상변경을 하려다 보니 개발/유지보수 담당자들의 기대와는 다르게 행동할 수 있게 된다. 말하자면 데이터베이스를 중심에 놓고 봤을 때 애플리케이션 담당자와 DBA는 공격과 방어의 입장 차이가 있게 된다는 것이다. 둘 중 어느 쪽이 우세하냐에 따라서 때때로 데이터베이스의 안정성은 심각한 영향을 받을 수 있는데, 필자가 다녀 본 여러 고객 사이트 중에도 DBA의 통제력이 약하거나 아예 통제 절차가 없는 경우도 상당수 있었으며, 안타깝지만 그러한 곳에서는 유독 성능 문제가 심각하게 나타나는 것이 일반적이었다.

이처럼 데이터베이스의 안정성을 보장하기 위한 중요한 수단의 하나로 통제절차는 반드시 고려할 사항이며, 통제절차가 너무 까다로우면 준수하기가 어렵고 너무 형식적이면 안정성에 아무런 도움이 되지 못한다. 이에 애플리케이션 담당자와 DBA가 상호간에 잘 준수할 수 있는 수준으로 적절하게 기준과 절차가 만들어져야 하며 양자 모두 절차 준수를 위한 의지와 절차 교육이 철저해야만 효과가 있다.

운영 통제는 사용자 데이터에 대해 적절한 통제가 이루어지고 있음을 증명하기 위한 ‘기록’을 필요로 하고, 이 기록의 내용에는 변경 일자와 변경 사유, 변경 내용을 비롯해 최초 요구자, 변경 신청자, 변경 처리자, 처리일자, 처리 결과, 검증 내용 등이 포함되며, 처리 결과 통제의 기준으로 가장 많이 사용되는 검증 방법은 성능기준에 대한 만족도와 표준에 대한 준수여부 검증이다. 예를 들면 온라인 화면의 응답속도는 조회 프로그램 기준으로 3초 이내이어야 한다든지 하는 성능 기준이 있을 수 있고, 또는 코딩 규칙으로 코딩시 코멘트나 적절한 메시지 사용 규칙, 에러 처리 규칙, 사용자 인터페이스 표준 등 시스템 개발과 관련한 여러 가지 표준 규정들을 잘 준수해 애플리케이션이 작성되었는지 등을 검사하는 것이 통제 기준의 한 사례라고 할 수 있다.

추가적으로 한 가지만 더 중요한 통제 요소를 든다면 해당 애플리케이션 변경에 따른 설계서 변경의 확인이다. 설계서에 변경 사항이 적절하게 반영되었는지 항상 확인하지 않으면 조만간 실행되고 있는 애플리케이션과 설계서가 서로 맞지 않아 결국 설계서는 아무도 들여다보지 않는 쓰레기로 전략하고 만다.

이러한 변경 기록의 작성과 검증 기준의 명시, 기준을 준수했는지를 검증하는 방법, 운영환경에 액세스하는 순서, 변경사항 반영 후의 행동 요령 등을 정의한 것을 통제 절차로 볼 수 있으며, 이 절차에 따라 업무를 수행함으로써 애플리케이션 담당자와 DBA 상호간에 명확한 업무 기준이 확보되고, 운영 데이터베이스는 안정적으로 서비스되면서 잘 자라날 수 있게 될 것이다.

성능 관리와 모니터링 그리고 진단과 튜닝

적절하게 통제되지 않은 애플리케이션이나 배치 작업 등에 의해, 또는 시스템 이상 발생에 의해 데이터베이스 서비스는 언제든지 장애가 발생할 수 있기 때문에, 이에 대한 모니터링과 주기적인 성능 진단 및 조치, 튜닝(성능개선) 활동은 DBA에게 있어서 매우 중요한 업무 요소가 된다. 사람으로 치면 주기적인 건강 진단과 적절한 치료가 여기에 해당된다고 할 수 있겠다.

<리스트 1> 모니터링에 발견된 비효율적인 SQL에 대한 원인 분석과 조치 사례

아무리 통제가 잘 이루어지고 있는 환경이라고 하더라도 데이터나 트랜잭션의 증가가 당초의 설계 기준을 상회하게 된다거나 미처 발견하지 못한 오류 요소들로 인해서도 성능 문제는 항상 발생할 소지를 안고 있다. 이 때문에 아무리 잘 설계되고 잘 개발된 시스템이라 할지라도, 또한 잘 통제되고 안정적으로 운영되고 있는 시스템이라 할지라도 지속적으로 성능 요소들을 모니터링하고 정기적인 진단 등을 통해 문제 요소를 사전에 도출해 내고 이를 적절하게 제거해 내는 일은 데이터베이스의 안정적인 운영에 있어서 중요한 업무이다.

DBA가 항상 모니터 앞에 앉아 성능 변화를 지켜보고 있는 것은 사실상 불가능하기 때문에 대부분은 자동화된 모니터링 소프트웨어(모니터링 툴)의 도움을 받게 되며, 이때 사용되는 소프트웨어의 기능이나 적용 범위 등에 의해, 또한 이를 사용하는 관리자의 기술 수준에 따라서도 결과는 판이하게 다를 수 있다. 그러나 모니터링 툴이 아무리 뛰어나도 여기에는 분명히 한계가 있을 수밖에 없기 때문에 가장 중요한 것은 이를 사용하는 사람이다. 즉, DBA가 툴이 제공하는 정보로부터 정확하게 문제 요소와 원인을 찾아낼 수 있어야 하며, 이를 위해 DBA는 전문성이 필요하다.

DBA를 ‘의사’라고 생각해보면 잘 이해가 갈 것이다. 선천적인 장애(설계부터 잘못된 경우)가 아닌 다음에는 훌륭한 의사가 있다면 건강에 대해 어느 정도 안심이 되듯이 잘 다듬어진 훌륭한 DBA는 데이터베이스의 건강 유지를 위해 꼭 필요한 요소라 할 수 있다. 수시로 성능진단을 해보고 원인을 추적해 보며 관련 서적이나 타인의 경험을 토대로 해결 방안을 찾고 조치하는 등의 훈련이 꾸준히 이루어져야만 툴에 의존하지 않고 사람이 올바르게 문제를 판단하고 적절하게 조치하는 것이 가능하며, 이러한 사람이 훌륭한 의사, 훌륭한 DBA라 할 수 있다.

<화면 2>처럼 서버에 갑작스럽게 심각한 부하가 발생하면 시스템 관리자는 신속하게 원인을 추적하고, 그 원인이 데이터베이스 쪽임이 판명되면 시스템 관리자는 DBA에게 원인 추적과 조치를 의뢰하게 된다. DBA는 원인 추적에 의해 부하의 원인이 <화면 2>에서와 같이 특정 SQL에 의한 것을 밝혀 낼 경우 이 SQL의 사용 목적과 사용 시점 등을 조사해 제거하거나 사용 시간대를 변경하도록 가이드해 주는 등의 조치를 통해 부하를 해소하게 된다. 또한 어떤 경우에는 저성능 SQL에 대한 실행계획을 분석해 비효율 원인을 도출해 내고, 직접 또는 해당 개발자나 유지보수 담당자에게 연락해 조치하게 함으로써 데이터베이스의 성능을 유지한다.

<리스트 1>은 모니터링에 발견된 비효율적인 SQL에 대한 원인 분석과 조치에 대한 사례이다. 이 경우는 memo_regdate 컬럼이 조건문에 사용될 때 to_char 함수로 가공되었기 때문으로 해당 인덱스를 사용하지 못하고 전체 테이블을 모두 검색하게 되어 매우 많은 I/O와 함께 이에 따른 응답속도 저하가 나타나게 된 사례이다. 여기서는 조건에 사용된 컬럼이 가공되지 않도록 상수 쪽을 가공해 조건문장을 재구성하면 된다.

<그림 3>은 진단/튜닝 업무를 수행하는 절차이며, 성능진단과 튜닝에 대한 방법에 대해서는 너무나 방대한 내용을 거론해야 하기 때문에 생략하기로 한다.

<그림 3> 시스템 성능진단/튜닝 절차 사례

장애대책 및 위험관리

데이터베이스 운영자는 다양한 장애 유형을 예측하고 평상시에 이에 대한 적절한 대응계획을 수립해 관련자들에 대해 교육·훈련을 실시함으로써 유사시에 대비해야 한다. 또한 다양한 위험요소에 대한 정의와 모니터링을 통해 장애 발생을 사전에 막을 수 있도록 함은 물론이고 유사시 신속하게 대응할 수 있는 비상조직 구성과 보고체계를 수립해 이러한 비상조직망에 대한 주기적인 점검 및 모의 훈련을 꾸준히 시행해야 한다. ‘비상시’는 장애상황과 재해상황으로 구분해 볼 수 있으며, 장애상황 발생시는 앞서 설명한 정규조직이 비상연락망을 가동해 신속하게 복구를 수행하고, 재해상황이 발생하면 앞서 설명한 비상조직이 가동되어 신속하게 상황을 극복하고 업무의 연속성을 확보할 수 있도록 해야 한다.

응급 환자가 응급실에 실려 왔을 때를 생각해 보자. 응급실에 있는 간호사며 의사들이 무슨 일을 해야 할지 모르고 허둥지둥하면 결국 환자의 생명은 보장할 수 없게 되는 것처럼, 비상 상황이 발생했을 때 관련자들이 훈련이 되어 있지 않아서 신속한 조치를 하지 못하고 허둥대게 되면 데이터베이스는 결국 조기에 생을 마감할 수밖에 없을 것이다. 이 때문에 평상시 비상 상황에 대한 대처 방안을 수립하고 관련자들에 대한 꾸준한 모의 훈련을 시행해야 하며, 이를 소홀히 하면 정작 중요한 순간에 눈물을 흘릴 수밖에 없을 것이다. 이는 앞서 설명한 것처럼 BCP/DRP로 불리는 업무 영역에서 주로 취급되기 때문에 더 이상의 설명은 생략하기로 한다.

평상시 정규조직의 업무 범위 내에서 자주 발생될 수 있는 장애상황과 이에 대한 조치를 RDBMS를 사용하고 있는 환경을 예로 들어 간략히 소개하면 <표 1>과 같다.

운영현황 보고

운영현황 보고는 사람으로 치면 정기 건강검진 보고와 같다. DBA는 운영현황 보고를 통해 운영 시스템의 구성 현황과 변동사항, CPU, 디스크, 메모리 등 주요 시스템 자원의 부하 현황, 장애발생 현황 등 운영하고 있는 시스템 자원에 대한 보고 의무에 수행하고, 데이터베이스에 발생하는 장애들을 분석해 각 장애 사이에 연관성이 있는지를 파악하며, 데이터베이스에 발생 가능한 추가적인 장애 요소들이 잠재하는지를 예측해 사전에 제거함으로써 데이터베이스가 안정적으로 운영되도록 해야 한다. 한편으로 장애발생 추이를 비롯해 평소 데이터 증가 추이를 잘 관찰해 저장공간 부족에 의해 시스템 가동이 중단되지 않도록 시기적절하게 디스크를 추가 또는 교체함으로써 데이터베이스가 항상 최상의 상태로 서비스될 수 있도록 하는 것도 DBA의 중요한 임무의 하나이다. 의사가 건강 검진한 결과들을 모아서 추이를 관찰함으로써 질병의 잠재 가능성을 찾아내고 미리 조치하는 것과도 같다고 하겠다.

이를 위해 DBA는 주기적으로 데이터베이스 운영 현황에 대한 자료를 수집해 이를 분석하고, 정기/부정기적으로 결과를 리포팅해 관리자 및 사용자 계층에 현황을 알리게 되는데, 이는 대개 전체 시스템에 대한 시스템 운영 현황 보고의 한 부분으로 이루어진다. 데이터베이스에 대한 운영현황 보고에서 주된 내용은 디스크 구성 및 사용량에 대한 현황, 데이터 저장량의 변화 추이, I/O 병목 발생 현황, 장애 발생 및 복구에 대한 기록 등을 포함하며, DBA는 이러한 보고를 통해 데이터베이스의 현재 운영 상태에 대한 정보를 주요 관련자를 포함한 조직 전체에 알려 공감대를 형성하고, 디스크 추가 또는 교체 시기 등의 예측을 통해 예산을 편성해 시기적절하게 구매를 추진하는 등 사전 대응을 용이하게 수행할 수 있다.


데이터베이스를 운영하는 사람

지금까지 데이터베이스의 운영 환경과 운영 통제 업무의 범위 등에 대해 설명했다. 데이터베이스 운영에 대해 마지막으로 논의할 중요한 사안은 운영의 주체인 ‘사람’이다. SF영화에 나오는 것처럼 완전히 자동화된 IT 환경이 아닌 다음에는 데이터베이스 관리의 궁극에는 사람이 있기 때문에, 결국 데이터베이스의 안정적인 운영 관리는 데이터베이스가 잘 운영되도록 관리하는 전문 인력의 구성과 전문성 정도에 영향을 받는다고 할 수 있다.

<표 1> 장애상황과 이에 대한 조치

대개 거의 모든 기업의 IT 부서는 데이터베이스를 운영·관리하기 위해 평상시 운영 업무를 담당하는 정규조직을 두고 있고 별도로 비상사태나 재해시 응급 복구 및 비상 운영을 위한 비상조직을 갖추고 있는데, 중요한 것은 이러한 조직 구성이 아니라 이들 각자가 상황에 맞게 충실히 역할을 수행할 수 있도록 평상시 이들에 대한 교육계획 수립과 지속적인 교육·훈련을 실시해 평상시나 유사시에 있어서 관련자들이 충분히 제 몫을 해내야 한다는 것이다.

정규조직의 보편적인 형태는 운영 시스템의 관리 업무 전체를 총괄하는 시스템 관리팀장을 두고 하드웨어, 소프트웨어 등을 관리하면서 이들에 대한 업그레이드 및 버전 관리, 장애 복구 및 성능 유지 관리 등의 업무를 수행하는 시스템 관리 파트와 네트워크 관련 하드웨어를 포함한 네트워크 구성과 회선 용량을 설계하고, 설치 및 유지 관리, 장애 복구 등의 업무를 수행하는 네트워크 관리 파트, 그리고 데이터베이스에 대한 형상관리 및 성능 유지, 장애 복구 등의 업무를 수행하는 DB 관리 파트 등으로 구분해 시스템 관리팀장이 각 담당자를 통해 이러한 업무들을 원활히 수행하도록 하고 있다. 앞에서 열심히 설명한 데이터베이스 운영 통제 관련 업무들은 바로 이 DB 관리 파트의 업무 내용이라고 보면 되겠다. 여기에 각 부문에서 심각한 장애가 발생했을 때 이를 신속하게 복구할 수 있도록 하드웨어 및 소프트웨어, 네트워크, DBMS 등에 대한 각 벤더 전문가들의 긴급 연락 전화번호를 중심으로 한 비상연락체계를 만들어 운영한다. <그림 4>는 정규조직과 비상조직의 구성 사례를 표시한 것이며, 비상조직은 앞서 얘기한 BCP/DRP의 업무 영역에 속하므로 여기서는 비상조직의 구성 사례만 소개하고 자세한 업무 내용에 대해서는 생략하였다.


데이터베이스 운영의 마지막 : 폐기

지금까지 데이터베이스를 운영·유지하는 여러 가지 업무 내용과 특징에 대해 살펴봤다. 시스템은 생명주기에 따라서 개발과 유지보수를 거쳐 용도 폐기라는 종착역에 이르게 되는데, 데이터베이스 역시 시스템의 생명주기에 따라 운명을 같이 하게 되는 것이 보통이다.

좀더 엄밀히 얘기하면 애플리케이션과 데이터베이스의 생명주기는 약간 다르다. 애플리케이션은 한번 개발되어서 성능 개선이나 요구사항 변경 등에 의해 유지보수가 일어나고 필요성이 소멸되면 그 애플리케이션도 더 이상 존재 가치가 없어지기 때문에 백업 후 시스템에서 제거해 내는 것이 보통이지만, 데이터베이스는 애플리케이션보다 생명주기가 길고 기업이 존재하는 한 해당 데이터가 계속해서 필요한 경우가 대부분이다. 물론 과거 데이터의 경우에 더 이상 접근할 필요가 없어져서 백업 후 시스템에서 삭제하는 경우도 있지만 그것은 어디까지나 일부 데이터에 국한된 얘기일 뿐 데이터베이스 전체에 대한 것은 아니다. 즉, 애플리케이션은 필요에 의해 얼마든지 재개발되고, 폐기, 변경 등이 일어날 수 있지만, 한번 생성된 데이터베이스는 그 기업 활동의 근간이 되기 때문에 쉽게 폐기되지 않는다. 그렇다면 데이터베이스 폐기는 어떤 경우에 일어날까?

<그림 4> 정규조직과 비상조직

데이터베이스 사용이 종료되는 상황으로는 시스템 재개발 또는 재구축 등에 의해 기존 데이터를 새로운 시스템으로 옮기고, 기존 데이터를 백업한 뒤 폐기하거나 또는 시스템 자체의 필요성이 소멸되어 데이터를 백업한 뒤 완전히 삭제 처리하는 경우 등이 있다.

첫 번째는 시스템 재개발이나 재구축에 따른 신규 시스템으로의 데이터베이스 전환에 해당하는 경우로, 앞의 데이터베이스 전환관리 업무 부분에서 이미 설명하였고, 두 번째는 해당 업무 자체가 완전히 없어지는 사례이므로 주로 기업 활동의 대상 분야가 변경되거나 기업의 존속 자체가 없어지는 경우라고 할 수 있다. 예를 들면 유통과 전자제품 조립을 주요 사업부문으로 하고 있던 어떤 기업이 구조조정으로 유통 부문을 완전히 정리해서 접기로 했다면 이제 더 이상은 이와 관련된 데이터베이스의 운영은 의미가 없어지게 되기 때문에 이러한 경우에 해당 데이터베이스가 폐기의 운명을 겪게 된다고 할 수 있고, 또는 회사가 부도나 경영상의 어려움으로 기업 활동을 완전히 종료하기로 했다면 이와 같은 경우에도 데이터베이스는 폐기의 운명을 겪게 될 것이다.

일단 데이터베이스를 폐기할 때는 적절하고 적법한 절차를 거쳐야 하는 것이 보편적이다. 항간에 일부 인터넷 사이트에서 사이트를 폐쇄하면서 회원정보를 다른 곳으로 빼돌려 많은 폐단이 발생하는 경우들을 보았을 것이다. 필자를 포함해서 독자 여러분들도 많은 인터넷 사이트에 회원으로 가입해 본 경험이 있을 텐데, 가입 동의문에는 반드시 고객의 정보를 보호하겠다는 내용의 문구가 포함되어 있는데, 바로 이 점 때문에 데이터베이스를 폐기할 때는 그 마지막 순간까지도 데이터베이스 내에 저장된 정보가 외부로 유출되지 않도록 주의를 기울이고 최선을 다해야 한다. 필자가 전산실에 근무하던 시절에도 이런 문제로 인해 보안 검사를 아주 강력하게 실시하곤 했었는데, 한번은 보안 검사팀이 쓰레기 소각장까지 모두 뒤져서 몇 조각으로 찢어진 문서 조각들을 찾아 퍼즐 맞추듯이 맞추어 내고는 결국 해당 문서의 폐기 처리자를 처벌했던 사례도 있었다. 이와 같이 정보의 폐기는 매우 중요시해야 하는 업무로, 나에겐 필요가 없어진 정보일지라도 다른 어떤 이에게는 매우 유용한 정보가 될 수 있음을 상기해 부주의로 인해 폐기한 정보가 외부로 유출되어 악용되지 않도록 주의해야 한다.

데이터베이스를 폐기할 때는 DBA가 해당 팀장 또는 관리자에게 폐기 사유와 폐기 일정, 방법, 폐기 장소, 확인 절차 등을 기재한 폐기 계획을 작성해 승인을 얻은 후에 계획에 따라 관리자 입회 하에 수행해야 한다.

폐기되는 데이터에 대해서는 보안 문제를 고려하여 어떠한 방법으로도 복구가 불가능하도록 인위적으로 자기적 훼손 방법을 사용하거나(자석으로 훑어 내려 데이터의 자기적 정렬을 파괴한다), 물리적인 손상이나 흠을 내서 판독이 불가능하게 할 수도 있고, 또는 관리자 입회 하에 소각 처리하는 등이 방법이 많이 사용되어 진다. 그냥 포맷하면 되지 않느냐고 반문할 수도 있겠으나, 요즈음은 기술의 발달로 포맷된 디스크도 얼마든지 복구가 가능하기 때문에 폐기 방법의 선택에 주의를 기울여야 한다.

앞에서 얘기한 것처럼 전환이나 폐기에 앞서 데이터는 일단 백업되어 일정기간 보관하게 되는데, 용도가 다한 데이터라 하더라도 법적 또는 재무적 목적에 의해 훗날에 다시 필요로 하게 될 수도 있기 때문이다. 보관기간은 최소 3년에서 5년, 10년 등으로 데이터의 성격이나 중요성 등에 따라 각자의 규칙을 정해 시행하게 된다. 백업된 과거 데이터는 저장 매체에 따라 영구 보관이나 주기적인 매체 교환 등을 통해 별도의 장소에서 보관되는 데, 이를테면 광 디스크 같은 매체는 영구보관용으로, 마그네틱 테이프는 일정기간 보관용으로 많이 사용된다. 도면이나 중요 서류 같은 것들은 오늘날 효용성이 많이 줄기는 했으나 마이크로필름 같은 것도 매우 유용한 저장매체로 활용될 수 있다.


우수한 DBA는 시스템 성능 유지와 직결

지금까지 데이터베이스의 운영부터 폐기에 이르기까지 다양한 운영 유지 업무와 그 특징들을 설명하였다. 처음 원고를 청탁받았을 때는 ‘매우 재미있겠다’는 생각으로 글을 쓰기 시작했는데, 막상 원고를 작성하다 보니 말해야 할 범위가 매우 넓고 내용도 깊어, 어떻게 수위를 조절해야 많지 않은 지면에 그래도 부족하지 않게 얘기를 풀어 낼 수 있을까 하는 걱정이 앞섰다. 사실 항목 하나하나가 너무나 깊은 내용을 갖고 있어 모두를 설명하기에는 지면이 부족했기에 설명의 수위를 조절하다 보니 어떤 부분은 ‘수박 겉핥기’식으로 지나가게 되어 부족한 점이 많다고 여겨진다. 그래도 앞서 서론 부분에서 언급한 것처럼 데이터베이스 관리 업무에 대해 궁금해 하거나 데이터베이스가 생성되어 운영 유지 과정을 거쳐 폐기에 이르기까지 어떤 일들을 하는지 알고자 하는 독자에게는 조금이라도 도움이 되었으면 하는 바람이다.

데이터베이스 운영에 대한 방법론이나 시각 등은 사실 이해 당사자들이나 기업에 따라 매우 다양하게 존재하기 때문에 이 글에서 소개한 내용들이 전부라 할 수 없거니와 또한 대표적인 사례라 말하기도 어렵다. 그래도 해당 업무의 일부는 될 것이기에 이러 이러한 일들이 진행된다고 감히 말한 것이며, 이 글을 읽는 독자들도 이 글에 소개된 업무가 데이터베이스 운영 업무의 전부라는 오해가 없기를 바란다.

다시 한번 말하거니와 데이터베이스 운영은 지식만으로 수행하기에 한계가 있으며, 많은 교육과 경험이 바탕이 되어야 하기 때문에 우수한 DBA를 양성하기 위해 많은 시간과 공을 들여야 한다. 우수한 DBA는 시스템의 성능 유지와도 직결된다고 할 수 있기 때문에 하드웨어에 대한 투자 못지 않게 사람을 키우는 데도 적절한 투자가 이루어져야 함을 다시 한번 강조하면서 이 글을 끝맺는다.

 

Posted by redkite
, |

대부분의 오라클 데이터베이스 사용자들은 유니버설 인스톨러(Universal Installer)에 의해 오라클 서버를 설치하게 되며 자신들의 데이터를 저장, 관리하는 일에 대부분의 시간과 노력을 투자하게 됩니다. 하지만, 자신들의 데이터를 안전하게 저장, 관리하기 위해서는 먼저, 오라클 서버의 기본구조를 명확히 이해해야 하며, 기본구조에 대한 안전한 구조설계를 해야 만 효과적으로 데이터를 저장, 관리할 수 있게 됩니다. 이번 섹션에서는 오라클 데이터베이스를 설치하고 난 후 반드시 고려해야 하는 구조설계에 대해 소개하고자 합니다.

 

 

 2장 오라클 DB의 논리적/물리적 구조의 이해

 

대부분의 오라클 데이터베이스 사용자들은 유니버설 인스톨러(Universal Installer)에 의해 오라클 서버를 설치하게 되며 자신들의 데이터를 저장, 관리하는 일에 대부분의 시간과 노력을 투자하게 됩니다. 

하지만, 자신들의 데이터를 안전하게 저장, 관리하기 위해서는 먼저, 오라클 서버의 기본구조를 명확히 이해해야 하며, 기본구조에 대한 안전한 구조설계를 해야 만 효과적으로 데이터를 저장, 관리할 수 있게 됩니다. 

이번 섹션에서는 오라클 데이터베이스를 설치하고 난 후 반드시 고려해야 하는 구조설계에 대해 소개하고자 합니다.

  2-1 오라클 DB의 논리적/물리적 구조

오라클 데이터베이스의 안전한 구조설계를 통해 데이터의 효과적인 저장, 관리를 하기 위해서는 먼저, 논리적, 물리적 구조에 대한 이해가 필수적입니다.

다음은 오라클 데이터베이스의 논리적 구조에 대한 설명입니다.


1) 데이터베이스

오라클 데이터베이스는 크게 논리적 구조와 물리적 구조로 나누어 구성되어 있습니다. 
일반적으로 사용자들이 말하는 데이터베이스(Database)는 논리적 개념에서 사용되는 용어이며, 데이터(Data)의 집합이란 의미를 가지고 있습니다. 
하지만, 오라클 서버에서 데이터베이스는 하나의 연속적인 공간을 일컫는 말은 아니며, 위 그림을 보시는 것처럼, 여러 개의 테이블스페이스(Tablespace)라는 논리적 구조가 모여서 하나의 데이터베이스를 구성하게 됩니다.

2) 테이블스페이스와 데이터 파일

하나의 데이터베이스를 구성하는 여러 개의 논리적 구조들을 테이블스페이스(Tablespace)라고 합니다. 일반적으로, 테이블스페이스는 유니버셜 인스톨러(Universal Installer)에 의해 기본적으로 3개(SYSTEM , UNDOTBS, TEMP 테이블스페이스)가 생성되며 또한, 사용자에 의해 추가적으로 여러 개의 테이블스페이스가 생성될 수도 있습니다.

(1) SYSTEM 테이블스페이스

데이터베이스는 기본적으로 자료사전(Data Dictionary) 테이블을 제공합니다. 데이터베이스 내에 생성되어 있는 모든 객체들에 대한 정보 뿐만 아니라 데이터베이스의 현재 상태를 보여주며 서버에 의해 생성되며 관리됩니다. 이와 같은, 자료사전 테이블들이 저장되는 논리적 구조를 SYSTEM 테이블스페이스 라고 합니다.

SQL> CONNECT system/manager 
SQL> SELECT * FROM DBA_USERS; 
SQL> SELECT * FROM DBA_DATA_FILES;

결론적으로 이 공간에는 데이터베이스의 모든 상태정보가 저장되어 있는 공간입니다.

(2) UNDOTBS 테이블스페이스

사용자들이 데이터베이스에 접속한 다음 DML문(Update, Delete, Insert)을 실행한 후 만약, 트랜잭션을 취소해야 한다면 ROLLBACK문을 수행하게 됩니다.

SQL> CONNECT scott/tiger
SQL> SELECT empno, ename, sal 
FROM emp WHERE empno = 7934;
SQL> UPDATE emp 
SET sal = sal * 1.1 WHERE empno = 7934;
SQL> SELECT empno, ename, sal 
FROM emp WHERE empno = 7934;
SQL> ROLLBACK;
SQL> SELECT empno, ename, sal 
FROM emp WHERE empno = 7934;

UPDATE문을 실행한 후 SELECT문을 수행하면 해당 사원의 SAL 컬럼 값이 변경되어 있는 것을 확인할 수 있습니다. 하지만, ROLLBACK문을 수행한 후 다시 SELECT문을 실행하면 UPDATE문을 실행하기 이전 상태로 돌아가 있는 것을 확인할 수 있을 것 입니다. 
그렇다면, ROLLBACK문을 실행하기 이전에는 변경 후의 값이, 실행하고 난 후에는 변경 전의 값을 확인할 수 있다는 것은 사용자는 알 수 없지만 어딘가에 변경 전의 값이 잠시 저장되어 있다는 것을 미루어 짐작할 수 있을 것 입니다. 
바로, 이 공간이 UNDOTBS 테이블스페이스 입니다. 결론적으로 이 공간에는 사용자의 ROLLBACK 데이터가 ROLLBACK 문장이 수행될 때까지 잠시 저장되어 있는 임시 공간입니다.


(3) TEMPORARY 테이블스페이스

UNDOTBS 테이블스페이스와 함께 사용자들이 자주 사용하는 것이 TEMP 테이블스페이스 입니다.

SQL> CONNECT scott/tiger
SQL> SELECT empno, ename, sal 
FROM emp 
ORDER BY deptno;

대부분의 사용자들이 데이터베이스에 접속하여 실행하는 문장들은 내부적으로 분류작업(Sorting)이 발생하게 됩니다. (ORDER BY, GROUP BY, DISTINCT, UNION CREATE INDEX~ 등) 
왜냐하면, 기본적인 테이블 구조들은 데이터를 입력했을 때 입력하는 순으로 저장되기 때문에 테이블의 정보를 SELECT 해 보면 그 결과를 쉽게 참조할 수 없는 단점을 가지고 있기 때문입니다. 결국, 그 데이터를 분류작업(Sorting) 하기 위해서는 별도의 임시공간이 필요하게 되는데, 바로 이 공간이 TEMP 테이블스페이스 입니다. 
결론적으로 이 공간에는 사용자의 SQL문이 분류작업을 요구하는 경우 잠시 사용하는 임시 공간입니다.

(4) 데이터 파일

지금까지 소개 드린 SYSTEM, UNDOTBS, TEMP 테이블스페이스는 다음과 같이 CREATE DATABASE ~ ; 문법에 의해 오라클 데이터베이스가 설치될 때 생성됩니다.

<xmp style="PADDING-BOTTOM: 5px; PADDING-LEFT: 15px; PADDING-RIGHT: 0px; BACKGROUND: #f4f4f4; PADDING-TOP: 5px"></xmp>CREATE DATABASE ora90 LOGFILE GROUP 1 ('c:\oracle\oradata\ora92\redo01.log') size 10m, GROUP 2 ('c:\oracle\oradata\ora92\redo02.log') size 10m) DATAFILE 'c:\oracle\oradata\ora92\system01.dbf' size 100m UNDO TABLESPACE undo DATAFILE 'c:\oracle\oradata\ora92\undo01.dbf' size 50m DEFAULT TEMPORARY TABLESPACE temp TEMPFILE 'c:\oracle\oradata\ora92\temp01.dbf' size 30m EXTENT MANAGEMENT LOCAL UNIFORM size 1m CHARACTER SET ko16ksc5601 NATIONAL CHARACTER SET al16utf16 SET TIME_ZONE = 'Korea/Seoul';

이 문법에서 SYSTEM01.DBF 데이터 파일은 SYSTEM 테이블스페이스에 대한 물리적 구조를 표현한 것이며, UNDO01.DBF 는 UNDOTBS 테이블스페이스, TEMP01.DBF는 TEMPORARY 테이블스페이스에 대한 물리적 구조를 표현한 것 입니다.


3) 세그멘트

데이터베이스 내에 생성되는 모든 객체( 테이블, 인덱스, 뷰, 시퀀스, 시노늄 등)들을 세그멘트(Segment) 라고 합니다. 다음 문법을 보시는 것처럼, 하나의 세그멘트는 하나의 테이블스페이스에 저장되는 하나의 구성요소 입니다.

SQL> CONNECT system/manager
SQL> CREATE TABLESPACE insa 
DATAFILE 'C:\ORACLE\ORADATA\ORA92\test01.dbf' size 10M;
SQL> CONNECT scott/tiger
SQL> CREATE TABLE test 
(a NUMBER(2), b CHAR(10)) 
TABLESPACE insa;
← TEST 테이블(세그멘트)는 INSA 테이블스페이스에 생성됩니다.

4) 익스텐트 

하나의 테이블스페이스가 여러 개의 세그멘트로 구성되어 있는 것처럼, 하나의 세그멘트도 여러 개의 익스텐트(Extent)가 모여서 구성됩니다. 하나의 테이블을 생성하면 처음부터 아주 큰 하나의 저장공간이 할당되는 것이 아니라, 처음에는 익스텐트라는 공간이 할당되고, 이 공간이 모두 사용되면 다시 익스텐트를 할당 받아 연속적으로 데이터를 저장하게 되는 것 입니다.

SQL> CONNECT scott/tiger
SQL> CREATE TABLE test1
  CREATE TABLE test1
  a NUMBER(2), b VARCHAR2(10))
  TABLESPACE insa
  STORAGE ( INITIAL 10K     ← 테이블의 최초 크기를 결정합니다.
    NEXT 10K     ← 최초 크기가 모두 사용된 후 
              다음 크기를 결정합니다.
    MINEXTENT 1
    MAXEXTENT UNLIMITED
    PCTINCREASE 50);

결론적으로, 하나의 테이블은 하나의 연속적인 공간으로 생성되는 것이 아니라 익스텐트라는 작은 구성요소로 생성되어 있는 것을 확인할 수 있습니다.

5) 블록 

오라클 데이터베이스의 가장 작은 저장구조를 블록(Block)이라고 합니다. 앞에서 소개 드린 익스텐트 구조는 사실은 하나의 연속적인 공간이 아니라 블록구조가 여러 개 모여 만들어지는 하나의 공간입니다. 결국, 여러 개의 블록이 모여 하나의 익스텐트를 만들게 됩니다.

그렇다면, 하나의 익스텐트를 왜 여러 개의 작은 구성요소로 만들었을까요 ??

만약, 하나의 익스텐트를 하나의 연속적인 공간으로 만들었다면 테이블의 데이터를 읽을 때 한번에 많은 데이터를 읽어야 하기 때문에 시간이 많이 소요될 뿐만 아니라 한번에 많은 데이터를 저장해야 하기 때문에 효과적인 저장과 관리가 용이하지 않기 때문입니다. 
그래서, 연속적인 데이터를 여러 개의 블록구조로 쪼개서 관리함으로써 효율성을 높이게 되는 것 입니다.

오라클 데이터베이스의 논리적, 물리적 저장구조에 대한 보다 자세한 설명은 "오라클 데이터베이스의 관리"에 관련된 참고서적을 참고해 주십시오.


사례-1

사용자의 데이터가 저장되어 있는 테이블에 입력, 수정, 삭제작업을 수행하다 보면 자주 발생하는 에러현상이 있습니다. 이 현상은 테이블이 저장되어 있는 테이블스페이스의 공간이 부족한 경우에 주로 발생합니다.

 ORA-01653 에러코드가 발생하는 경우
 테이블스페이스를 생성 또는 변경하거나 크기를 관리해야 경우

1) 테스트를 위해 TEST 테이블스페이스를 생성하십시오.
[C:\] sqlplus "/as sysdba"
SQL> startup force
SQL> create tablespace test 
datafile 'c:\oracle\oradata\ora92\test_01.dbf' size 3M;
SQL> select tablespace_name, bytes, file_name from dba_data_files;

2) TEST 테이블을 TEST 테이블스페이스에 생성하십시오.
[C:\] sqlplus "/as sysdba"
SQL> create table test(name char(30)) 
tablespace test 
storage(initial 2M);

3) TEST 테이블이 생성되었으면 연속적으로 행들을 입력하여 익스텐트 확장 시 에러가 발생하도록 하십시오. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> declare x number(2); begin for x in 1 .. 10000 loop insert into test values('111111111111111111111111111111'); end loop; end; / SQL> / SQL> / SQL> / ← 익스텐트 에러가 발생할 때 까지 반복적으로 실행하십시오. SQL> / declare * 1행에 오류: ORA-01653: SYS.TEST 테이블을 128(으)로 TEST 테이블스페이스에서 확장할 수 없습니다 ORA-06512: 줄 5에서
4) 테이블스페이스의 크기가 부족하여 발생하는 에러를 해결하기 위해서는 해당 테이블스페이스에 새로운 데이터 파일을 추가하는 방법입니다. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> alter tablespace test add datafile 'c:\oracle\oradata\ora92\test_02.dbf' size 3M; SQL> declare x number(2); begin for x in 1 .. 10000 loop insert into test values('111111111111111111111111111111'); end loop; end; / SQL> select tablespace_name, bytes, file_name from dba_data_files; SQL> host dir c:\oracle\oradata\ora92 → "test_02.dbf" 파일의 존재여부와 파일 크기를 확인하십시오. test_01.dbf test_02.dbf
5) 테이블스페이스의 크기를 늘리는 두 번째 방법은 기존의 데이터 파일을 RESIZE절로 늘리는 방법입니다. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> alter database datafile 'c:\oracle\oradata\ora92\test_02.dbf' resize 5M; SQL> select tablespace_name, bytes, file_name from dba_data_files; SQL> host dir c:\oracle\oradata\ora92
6) 실습이 끝나면 TEST 테이블스페이스를 삭제하십시오. 해당 테이블스페이스 내에 관련 테이블이나 인덱스가 생성되어 있으면 테이블스페이스를 삭제할 수 없습니다. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> drop tablespace test; drop tablespace test * 1행에 오류: ORA-01549: 테이블스페이스가 비어있지 않으므로 INCLUDING CONTENTS 옵션 을 사용해 주십시오 SQL> drop tablespace test including contents; SQL> select tablespace_name, bytes, file_name from dba_data_files; SQL> host dir c:\oracle\oradata\ora92 test_01.dbf test_02.dbf
7) 테이블스페이스를 삭제하더라도 관련 데이터 파일은 삭제되지 않습니다. 추가적인 작업을 통해 관련 데이터 파일을 함께 삭제하십시오. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> host del c:\oracle\oradata\ora92\test_0* SQL> host dir c:\oracle\oradata\ora92 SQL> exit
사례-2

사용자들이 실행하는 SQL문장 중에는 SORTING(분류작업)이 유발되는 경우가 매우 많습니다. 동시에 많은 사용자들이 이러한 문장을 실행하게 되면 Temporary 테이블스페이스를 통해 분류작업을 수행하게 되는데 만약 TEMPORARY 테이블스페이스의 데이터 파일이 유실된다면 더 이상 작업이 진행되지 못하여 장애가 발생하게 됩니다. 이런 경우, 어떻게 장애를 복구해야 할까요 ?


1) 이 실습을 수행하기 위해 오라클 데이터베이스가 정상이라는 것을 확인하십시오. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>[C:\] sqlplus "/as sysdba" SQL> startup SQL> select tablespace_name, file_name from dba_temp_files; → 현재 오라클 서버환경에서 TEMPORARY 테이블스페이스의 이름과 위치를 분석하십시오. SQL> host dir c:\oracle\oradata\ora92\temp02.dbf
2) 다음과 같은 실습 시나리오를 통해 TEMP 테이블스페이스에 장애가 발생 합니다. <xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> shutdown abort → 오라클 서버가 비정상적으로 종료됩니다. 갑자기, 정전이 발생하여 시스템이 종료되는 것과 동일한 시나리오를 연출하기 위함 입니다. SQL> exit [C:\] cd c:\oracle\oradata\ora92 [C:\] move temp02.dbf temp02.org → Temp 테이블스페이스가 디스크의 장애로 인해 읽기, 쓰기 작업이 수행되지 못하는 경우를 연출하기 위해 임의로 이동됩니다. [C:\] sqlplus "/as sysdba" SQL> startup mount SQL> alter database open; → TEMP 테이블스페이스에 장애가 발생하여 더 이상 읽기,쓰기 작업을 수행할 수 없지만 오라클 서버는 정상적으로 오픈 됩니다. 왜냐하면, TEMP 테이블스페이 스는 사용자의 데이터가 저장되는 공간이 아니라 SQL문을 실행할 때 발생하는 SORTING 데이터가 잠시 저장되는 임시 공간이기 때문에 직접적인 복구가 요구 되지 않기 때문에 오라클 서버는 정상적으로 오픈되는 것 입니다. 오라클 서버 가 오픈 된다는 것은 TEMP 테이블스페이스가 사용 가능하다는 것을 의미하는 것은 아니며, 오픈 후 복구작업을 수행해야 합니다.
3) 다음은 TEMP 테이블스페이스를 복구하는 방법과 절차입니다.

TEMP 테이블스페이스에는 사용자의 데이터가 저장되어 있지 않으므로, 데이터에 대한 복구는 요구되지 않으며 다만, TEMP 테이블스페이스의 구조를 재생성 하는 것이 복구작업의 모든 것 입니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> select * from dba_tablespaces; → 자료사전 테이블에는 Temp" Tablespace는 그래도 존재합니다. 왜냐하면 Temp Tablespace의 구성 File 중 하나를 Drop한 것 뿐이기 때문입니다. SQL> drop tablespace temp including contents; → DROP 문장을 실행하면 컨트롤 파일 내에 존재하는 TEMP 테이블스페이스에 대한 정보와 데이터 딕션어리 테이블에 저장되어 있는 정보가 모두 삭제됩니다. SQL> create temporary tablespace temp2 tempfile 'c:\oracle\oradata\ora92\temp02.dbf' size 3m; → CREATE 문장을 실행하면 컨트롤 파일에 TEMP 테이블스페이스에 대한 정보가 저장되며 데이터 딕션어리 테이블에도 관련 정보가 저장됩니다. 컨트롤 파일에 관련 정보를 저장하는 이유는 데이터베이스의 무결성을 보장하기 위해서 입니다. SQL> host dir c:\oracle\oradata\ora92\temp02.dbf → 해당 경로에서 파일과 크기 확인 SQL> shutdown immediate SQL> exit [C:\] del c:\oracle\oradata\ora92\temp02.org → 이전 TEMP 파일은 불필요함으로 삭제 [C:\] cd
2-2. 컨트롤 파일
2-2-1 다중 컨트롤 파일 시스템 이란?

오라클 데이터베이스를 설치하면 기본적으로 생성되는 파일들이 있습니다. 이 중에 컨트롤 파일은 별도의 문법에 의해 생성되지는 않으며 단지, CREATE DATABASE ~ 문법에 의해서만 자동 생성되고 데이터베이스 생성 시 모든 상태 정보를 저장하게 됩니다. 
(파일명과 경로, 시스템 변경번호, 로그-시퀀스 번호, 데이터베이스 명, 설치날자, Characterset 등) 
또한, 데이터베이스 생성 시 오라클 서버는 init.ora 파라메터 파일에 정의되어 있는 CONTROL_FILES 파라메터 값을 참조하여 정해진 경로에, 정해진 파일이름으로, 정해진 개수 만큼의 컨트롤 파일을 생성하게 됩니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>[C:\] CD C:\ORACLE\ORA92\DATABASE [C:\] EDIT init.ora CONTROL_FILES = ("C:\ORACLE\ORADATA\ORA92\CONTROL01.CTL", "C:\ORACLE\ORADATA\ORA92\CONTROL02.CTL")

예를 들어, 위 문법과 같이 2개의 컨트롤 파일에 대한 정보를 init.ora 파일에 정의하였다면, 데이터베이스 생성 시 오라클 서버는 컨트롤 파일을 정해진 위치에 정해진 이름으로 2개를 생성해 줍니다. 이때, 첫 번째 컨트롤 파일이 원본이며, 두 번째 파일은 미러링(Mirror) 컨트롤 파일입니다. 만약, init.ora 파일에 3 번째 컨트롤 파일에 대한 정보를 정의하였다면, 생성 후 하나의 원본 컨트롤 파일과 2개의 미러링 컨트롤 파일을 확인할 수 있을 것 입니다. 이러한 구조를 다중 컨트롤 파일 시스템(Multipled Control File System) 이라고 합니다. (기본적으로 오라클 서버는 사용자가 init.ora 파일에 컨트롤 파일에 대한 정보를 정의하지 않으면 1개의 원본 컨트롤 파일만 생성해 줍니다.)

결론적으로, 오라클 서버가 하나의 원본 컨트롤 파일과 여러 개의 미러링 컨트롤 파일을 제공하는 이유는 무엇일까요 ?

그 이유는 , 컨트롤 파일은 데이터베이스를 사용하면서 없어서는 안될 가장 중요한 파일 중에 하나이기 때문에 무엇보다도 잘 관리해야 만 오라클 데이터베이스의 안정성이 보장될 수 있기 때문 입니다.

1) 데이터베이스의 시작단계에서 반드시 필요한 컨트롤 파일

데이터베이스를 시작할 때(STARTUP), NOMOUNT 단계가 끝나고 MOUNT 단계가 되면 컨트롤 파일 정보를 읽어 데이터베이스의 모든 파일 상태를 검증하게 되는데 만약, 컨트롤 파일을 읽을 수 없다면(사용자의 실수로 삭제되거나 디스크의 이상으로 생기는 문제) 데이터베이스는 더 이상 MOUNT 작업을 수행하지 않으며, 사용자들은 데이터베이스를 사용할 수 없게 됩니다. (1편 "오라클 서버의 시작과 종료"를 참조하십시오.)

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>[C:\] CD C:\ORACLE\ORADATA\ORA92 [C:\] DEL *.CTL ← 모든 컨트롤 파일이 삭제됩니다. [C:\] sqlplus SQL*Plus: Release 9.0.1.0.0 - Production on Tue Mar 2 10:33:39 2004 (c) Copyright 2001 Oracle Corporation. All rights reserved. Enter user-name: /as sysdba Connected to an idle instance. SQL> startup ORACLE instance started. Total System Global Area 26140792 bytes Fixed Size 434296 bytes Variable Size 20971520 bytes Database Buffers 4194304 bytes Redo Buffers 540672 bytes ORA-00205: error in identifying controlfile, check alert log for more info
2) COMMIT문을 실행할 때 SCN을 기록하기 위해 반드시 필요한 컨트롤 파일

1편 "COMMIT문의 처리과정"에서 소개한대로 컨트롤 파일의 용도는 사용자들이 DML문을 실행한 후 COMMIT문을 실행할 때 데이터베이스의 상태정보를 기록해 두는 파일입니다. 
만약, 이 파일을 일기, 쓰기 할 수 없다면 더 이상 사용자들은 DML문을 실행할 수 없게 됩니다. (1편 "COMMIT문의 처리과정"을 참조하십시오.)

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>[C:\] sqlplus SCOTT/TIGER SQL> UPDATE emp SET sal = sal 1.5; SQL> COMMIT; SQL> CONNECT /AS SYSDBA SQL> SHUTDOWN ORA-00210: cannot open the specified controlfile ORA-00202: controlfile: 'C:\ORACLE\ORADATA\ORA92\CONTROL01.CTL' ORA-27041: unable to open file SVR4 Error: 2: No such file or directory Additional information: 3

결론적으로, 컨트롤 파일은 항상 잘 관리해야 하고 문제가 발생했을 때 쉽게 대처할 수 있도록 평소에 백업을 잘 해 두는 것을 습관화 해야 합니다. 
WINDOWS 환경에서 유니버설 인스톨러에 의해 오라클 데이터베이스를 설치하면 기본적으로 3개의 컨트롤 파일이 생성됩니다. 첫 번째 파일이 원본 컨트롤 파일이며 두 번째, 세 번째 컨트롤 파일은 원본에 대한 미러링(MIRRORING) 파일입니다. 원본 컨트롤 파일에 문제가 발생하면 두 번째, 세 번째 파일로 쉽게 복구할 수 있도록 오라클 서버가 기본적으로 제공하는 것 입니다.(UNIX 환경에서는 기본적으로 하나의 컨트롤 파일이 생성됩니다.) 
현재, 데이터베이스에서 사용하고 있는 컨트롤 파일의 위치와 이름을 알고 싶다면 init.ora 파일의 CONTROL_FILES 파라메터를 참조하십시오.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>[C:\] CD C:\ORACLE\ORA92\DATABASE [C:\] EDIT init.ora ########################################### # 파일 구성 ########################################### control_files=( "C:\oracle\oradata\ORA92\CONTROL01.CTL", ← 원본 컨트롤 파일 "C:\oracle\oradata\ORA92\CONTROL02.CTL", ← 미러링 컨트롤 파일 "C:\oracle\oradata\ORA92\CONTROL03.CTL") ← 미러링 컨트롤 파일

또는, 다음과 같이 V$CONTROLFILE 자료사전을 참조해 보십시오.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> CONNECT system/manager SQL> SELECT * FROM V$CONTROLFILE; STATUS NAME --------------------------------------------- C:\oracle\oradata\ORA92\CONTROL01.CTL C:\oracle\oradata\ORA92\CONTROL02.CTL C:\oracle\oradata\ORA92\CONTROL03.CTL
2-3. 리두로그 파일
2-3-1 다중 리두로그 파일 시스템의 설계와 방법

이번에는 오라클 데이터베이스의 안정적 운영을 위해 요구되는 구조적 설계에서 가장 중요한 다중 리두로그 파일 시스템(Multipled Redo-Log File System)에 대해서 자세히 알아 보도록 하겠습니다.

1) 리두로그 파일 시스템

사용자의 SQL문에 대해 COMMIT문을 실행하면 SQL*PLUS 화면에 "커밋이 성공적이다"라는 메시지를 확인할 수 있습니다. 이 메시지의 의미는 커밋 되었던 모든 행 데이터(변경 전 데이터와 변경 후 데이터)를 리두로그 버퍼 영역에 백업했다는 의미이며, 이어 LGWR 백그라운드 프로세스가 일시적으로 저장되는 리두로그 버퍼 영역의 모든 데이터를 영구히 저장할 수 있는 리두로그 파일로 저장했다는 의미입니다. 결론적으로, 현재 변경한 행 데이터를 리두로그 파일로 백업했다는 의미를 가지게 됩니다. 리두로그 버퍼와 리두로그 파일에 모든 변경정보를 저장하는 이유는 갑작스런 시스템의 다운(DOWN) 또는 갑작스런 데이터베이스의 다운 시 처리하고 있던 모든 작업 내용이 테이블에 미쳐 저장되기 전이라면 데이터의 유실이 발생할 수 있기 때문입니다. 이때 리두로그 파일에 백업해 둔 데이터를 통해 데이터베이스를 복구하기 위해서 입니다.

사용자가 생성한 테이블에 대해 DML문을 실행할 때 모든 데이터가 리두로그 버퍼에 백업될 수 있는 이유는 해당 테이블이 생성될 때 LOGGING 문법 절에 의해 결정됩니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> CONNECT SCOTT/TIGER SQL> CREATE TABLE TEST1 (NO NUMBER, NAME VARCHAR2(15)) LOGGING;

테이블을 생성할 때, LOGGING 절을 부여하지 않으면 오라클 서버는 기본적으로 LOGGING 절을 적용해 줍니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> CREATE TABLE TEST2 (NO NUMBER, NAME VARCHAR2(15)) LOGGING;

만약, 해당 테이블에 대해서는 DML문이 실행될 때 모든 데이터를 백업하지 않아도 된다면 다음과 같이 NOLOGGIN 절을 사용할 수도 있습니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> CREATE TABLE TEST3 (NO NUMBER, NAME VARCHAR2(15)) NOLOGGING;

일반적으로, 다른 데이터베이스 환경에서 다운로드를 통해 재 사용되는 테이블이거나, 언제든지 해당 테이블에 대한 복구가 용이한 경우에는 NOLOGGING절을 사용할 수도 있습니다. 
하지만, 이러한 경우가 아닌 경우에는 데이터베이스 차원에서 모든 데이터들에 대한 백업정보를 로그버퍼에 저장해 주는 것이 기본 모드입니다.

자 ~ 그럼 리두로그 파일의 구조에 대해서 조금 더 자세히 알아 보도록 하겠습니다.

기본적으로 CREATE DATABASE 문에 의해 오라클 데이터베이스를 생성하면 최소 2개의 리두로그 파일이 생성되며, 운영체계에 따라 차이는 있지만, 기본적으로 3개의 리두로그 파일이 생성됩니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> CREATE DATABASE ora90 LOGFILE GROUP 1 ('c:\oracle\oradata\ora92\redo01.log') size 100m, GROUP 2 ('c:\oracle\oradata\ora92\redo02.log') size 100m, GROUP 3 ('c:\oracle\oradata\ora92\redo03.log') size 100m) DATAFILE 'c:\oracle\oradata\ora92\system01.dbf' size 100m UNDO TABLESPACE undo DATAFILE 'c:\oracle\oradata\ora92\undo01.dbf' size 50m DEFAULT TEMPORARY TABLESPACE temp TEMPFILE 'c:\oracle\oradata\ora92\temp01.dbf' size 30m EXTENT MANAGEMENT LOCAL UNIFORM size 1m ;

또는, V$LOGFILE 자료사전을 통해서도 확인할 수 있습니다.

<xmp style="PADDING-BOTTOM: 10px; PADDING-LEFT: 10px; PADDING-RIGHT: 10px; BACKGROUND: #f4f4f4; PADDING-TOP: 10px"></xmp>SQL> SET LINESIZE 1000 SQL> SELECT * FROM V$LOGFILE; GROUP# STATUS TYPE MEMBER ---------- -------- ------- ------------------------------------ 1 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO01.LOG 2 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO02.LOG 3 ONLINE C:\ORACLE\ORADATA\ORA92\REDO03.LOG
2) 다중 리두로그 파일 시스템

이러한 리두로그 파일은 오라클 데이터베이스에 장애가 발생한 경우 복구작업을 수행하기 위해서는 반드시 필요한 백업 데이터를 저장하고 있기 때문에 철저한 관리가 요구됩니다. 
그런데, 리두로그 파일이 저장되어 있는 디스크에 장애가 발생한다면 어떻게 될까요 ? 
결론적으로 말하자면, 복구 시 사용해야 할 백업 데이터에 문제가 발생한 경우이므로, 만약, 데이터 파일에 대한 복구가 필요하다면, 복구작업을 수행할 수 없게 됩니다. 즉, 데이터의 유실이 발생하게 됩니다. 
그래서, 오라클 사에서는 원본 리두로그 파일에 장애가 발생할 것을 대비하여 미러링(Mirroring) 리두로그 파일을 생성하게 함으로써, 어떤 장애가 발생하더라도 모든 데이터에 대한 복구작업을 수행할 수 있기 해 줍니다. 
이러한 데이터베이스 환경을 다중 리두로그 파일 시스템(Multiplex Redo-Log File System)이라고 합니다. 개념적으로, 다중 컨트롤 파일 시스템(Multiplex Control File System)과 같이 원본 파일에 대한 복사본 파일을 항상 유지해 둠으로써 원본에 문제가 발생하면 복사본 파일로 대체하여 오라클 데이터베이스를 사용 가능하게 해 줍니다.

다음은 다중 리두로그 파일 시스템을 구축하는 방법과 절차입니다.

(1) 먼저, 현재 사용중인 데이터베이스 환경에서 리두로그 파일 상태를 확인하십시오. <xmp style="PADDING-LEFT: 25px"></xmp>[C:\] sqlplus "/as sysdba" SQL> SELECT * FROM V$LOG; GROUP# THREAD# SEQUENCE# BYTES MEMBERS ARC STATUS ---------- ---------- ---------- ---------- ---------- --- --------- 1 1 63 5242880 1 NO INACTIVE 2 1 62 5242880 1 NO INACTIVE 3 1 64 1048576 1 NO CURRENT ← MEMBERS 컬럼의 값 1은 다중 리두로그 파일 시스템이 아닌 것을 의미합니다. 만약, 다중 리두로그 파일 시스템이라면 2 이상의 값을 확인할 수 있습니다.(2) 현재, 데이터베이스 환경은 몇 개의 리두로그 그룹을 가지고 있는지 확인하십시오.
    그리고, 새로운 그룹을 추가하십시오. 
<xmp style="PADDING-LEFT: 25px"></xmp>SQL> ALTER DATABASE ADD LOGFILE GROUP 4 'C:\ORACLE\ORADATA\ORA92\REDO04.LOG' size 500k;
(3) 새로운 그룹을 추가하였으면, 다중 리두로그 파일 시스템을 구축해 보십시오.
    그리고,리두로그룹 4에 대한 미러링 파일을 추가 생성합니다. 
<xmp style="PADDING-LEFT: 25px"></xmp>SQL> ALTER DATABASE ADD LOGFILE MEMBER 'C:\ORACLE\ORADATA\ORA92\REDO04B.LOG' TO GROUP 4;
(4) 이번에는 리두로그 그룹1, 그룹2, 그룹3에 대한 각각의 미러링 파일을 추가하십시오. <xmp style="PADDING-LEFT: 25px"></xmp>SQL> ALTER DATABASE ADD LOGFILE MEMBER ] 'c:\oracle\oradata\ora92\REDO01B.LOG ' TO GROUP 1; SQL> ALTER DATABASE ADD LOGFILE MEMBER 'c:\oracle\oradata\ora92\REDO02B.LOG' TO GROUP 2; SQL> ALTER DATABASE ADD LOGFILE MEMBER 'c:\oracle\oradata\ora92\REDO03B.LOG' TO GROUP 3; SQL> SELECT * FROM v$logfile; GROUP# STATUS TYPE MEMBER ------ ------- ------- ---------------------------------------- 1 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO01.LOG 1 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO01B.LOG 2 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO02.LOG 2 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO02B.LOG 3 ONLINE C:\ORACLE\ORADATA\ORA92\REDO03.LOG 3 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO03B.LOG 3 ONLINE C:\ORACLE\ORADATA\ORA92\REDO04.LOG 3 STALE ONLINE C:\ORACLE\ORADATA\ORA92\REDO04B.LOG SQL> SELECT * FROM V$LOG; GROUP# THREAD# SEQUENCE# BYTES MEMBERS ARC STATUS ---------- ---------- ----------- --------- -------- --- ---------- 1 1 62 5242880 2 NO INACTIVE 2 1 63 5242880 2 NO INACTIVE 3 1 64 1048576 2 NO CURRENT 4 1 61 1048576 2 NO CURRENT

 

Posted by redkite
, |

오라클과 NLS의 찰떡궁합 들여다보기(2)

 

이번 회는 캐릭터셋을 변경하는 작업에 대한 준비와 그 방법에 대한 것을 알아보고자 한다. 캐릭터셋 변경은 미래의 시스템 확장과 개발의 용이함을 위해 권장되는 작업이지만, 실제 위험성이 크기 때문에 매우 조심해야 한다. 그래서인지 OTN의 NLS 포럼에서도 빈번히 등장하는 질문이기도 한 "캐릭터셋 변경"에 대해 이번 회에서 집중적으로 다루어보기로 하겠다.

  1. 잘못된 캐릭터셋을 사용해온 시스템, 치료해야 하나?
    1. 캐릭터셋 오용의 예
    2. 권장 사항
      1. 1. 바로잡기의 필요성을 인식하라
      2. 2. 절대 함부로 변경하지 말라
  2. 캐릭터셋을 변경 방식과 위험성
    1. 캐릭터셋 변경이란?
      1. 변경 케이스 1) 캐릭터셋 딕셔너리 정보 변경 + 데이터 불변
      2. 변경 케이스 2) 캐릭터셋  딕셔너리 정보 변경 + 데이터 변경
    2. 캐릭터셋 변경의 위험성
      1. 1) 데이터 절삭
      2. 2) 데이터 깨짐
        1. US7ASCII 데이터베이스의 한글 데이터를 exp/imp를 이용하여 KO16MSWIN949 또는 UTF8로 마이그레이션
        2. KO16KSC5601 데이터베이스의 한글 데이터를 exp/imp를 이용하여 KO16MSWIN949 또는 UTF8로 마이그레이션
      3. 3) 애플리케이션 오동작
  3. 캐릭터셋 변경의 실제
    1. ALTER DATABASE 명령을 이용한 딕셔너리 변경
      1. 절차
    2. 데이터 마이그레이션
      1. 준비작업
      2. CSSCAN 실행
        1. 1) CSSCAN 설치
        2. 2) CSSCAN 구동
      3. exp/imp를 이용한 변경
      4. CSALTER를 이용한 변경
  4. 글을 마치며



연.재.순.서.

1회 : 오라클과 NLS의 찰떡궁합 들여다보기(1)
2회 : 오라클과 NLS의 찰떡궁합 들여다보기(2)
3회 : 오라클 GDK를 사용하여 깔끔한 다국어 개발 유틸리티를 만들자
4회 : 한글화된 오라클 제품, 그 이면의 비밀.

잘못된 캐릭터셋을 사용해온 시스템, 치료해야 하나?

캐릭터셋 오용의 예

캐릭터셋과 전혀 맞지 않는 데이터를 저장하고 검색할 수 있을까? 정답은 당연히 없다. 그렇지만, 현실적으로 많은 시스템의 데이터베이스에 그런 데이터가 저장되어 있을 것이라고 믿고 있다.

  1. 오용 1) US7ASCII 데이터베이스에 한글을 저장한 경우
  2. 오용 2) US7ASCII 데이터베이스에 한글, 중국어, 일본어 등 다양한 언어를 저장한 경우
  3. 오용 3) KO16KSC5601 데이터베이스에 확장 한글(비완성형)을 저장한 경우

이 세 가지 경우는 잘못된 캐릭터셋을 사용하고 있는 경우 중 문제가 발생할 만한 대표적인 경우들이다. 다음 표는 이 경우들에 대해 발생할 수 있는 문제점과 취할 수 있는 치료방법을 요약하고 있다.


문 제 감시 시점
지 속적 사용 가능 여부
해 결책
오용1
1) 다른 정상적인 데이터베이스와 DBLINK로 연결시 한글 전달 실패
2) 캐릭터 관련 함수들의 예측할 수 없는 결과
3) 다른 캐릭터셋으로 변경 시도시 데이터 마이그레이션 실패
4) exp/imp를 사용해 다른 데이터베이스로 데이터 마이그레이션 실패
1) 똑같이 잘못된 구성을 가진 데이터베이스 집합 시스템에서는 지속 운영 가능

2) 정상적인 데이터베이스와 정상적인 통신은 불가능함

3) 정상적은 데이터베이스로 업그레이드시 난관 봉착
Full Backup이 필요없다고 절대 말하지 말라!

오라클 엔지니어로 하여금 캐릭터셋을 강제로 KO16MSWIN949로 변경하게 함으로써 해결할 수 있다.

단, 변경 전에 정말 데이터들이 순수하게 KO16MSWIN949라는 한 캐릭터셋으로 커버되는 것이 맞는지 확인해야 한다. 그렇지 않으면 "예 2"의 경우가 된다.

이런 캐릭터셋 변경은 오라클 엔지니어나, 지원 계약을 맺은 고객이 충분히 엔지니어로부터 위험성에 대해 설명을 들은 후에야 시도할 수 있다는 사실을 잊지 말기 바란다. 그렇지 않은 시도는 누구도 책임져 주지 않는다.

캐릭터셋 변경 후 애플리케이션의 디버깅은 필수이다.
오용2
1) 다른 데이터베이스와 DBLINK로 연결시 한글 전달 실패
2) 캐릭터 관련 함수들의 예측할 수 없는 결과
3) 다른 캐릭터셋으로 변경 시도시 실패
4) exp/imp를 사용해 다른 데이터베이스로 데이터 마이그레이션 실패
1) 똑같이 잘못된 구성을 가진 데이터베이스 집합 시스템에서는 지속 운영 가능

2) 정상적인 데이터베이스와 정상적인 통신은 불가능함

3) 정상적은 데이터베이스로 업그레이드시 난관 봉착
최악의 케이스. 다양한 캐릭터셋이 한자리에 모인 잔치집으로, 어떤 한 캐릭터셋으로의 강제 변환이 의미없다.

그나마 각 언어별로 데이터를 구분하여 조회할 수 있다면, 한국어는 한국어별로, 중국어는 중국어별로 physical OS file로 덤프를 떠서 해결하기를 권장한다.

각 언어별로 Manual하게 고쳐나가는 것 밖에는 도리가 없다.

Physical dump file은 SQL Loader를 통해 새로운 UTF8 데이터베이스에 안전하게 로딩할 수 있다. 물론 지난 호에 나온 대로 각 언어별로 NLS_LANG변수값은 확실히 설정해주어야 한다.

SQL Loader를 사용할 때 NLS_LANG값은:
  • 한국어 파일 업로드시 : .KO16MSWIN949
  • 중국어간체 : .GB2312
  • 중국어번체 : .ZHT16MSWIN950 또는 .BIG5

이런 내용은 자신이 없거나, 책임질 수 있는 위치가 아니면, 절대 함부로 시도해서는 안된다.

데이터베이스를 제대로 고쳤을 경우에는 애플리케이션도 재개발 수준의 디버깅을 해야 한다.
오용3
1) exp/imp를 사용해 다른 데이터베이스로 데이터 마이그레이션 실패
2) 가끔 이런 데이터베이스를 기반으로 한 애플리케이션에서 깨진 글자들을 발견하게 됨(종종 우리나라 게시판들에서 볼 수 있는 현상)
3) 운영자들이 자신의 시스템은 정상이라고 생각하는 경우가 많이 감지가 어려움
4) KO16KSC5601(완성형)이 우리나라 한글을 대표하는 캐릭터셋이라는 인식이 문제
1) 일부 비완성형 한글 데이터들의 처리를 주의하면 지속 사용할 수 있고, ,다른 KO16MSWIN949, UTF8기반의 정상적인 한글 데이터베이스와도 어느 정도 통신이 가능하다

KO16KSC5601에서 KO16MSWIN949로 캐릭터셋을 변경한다. KO16MSWIN949는 KO16KSC5601의 수퍼셋이므로 이러한 변경은 자연스럽다. 가장 쉽게 해결될 수 있는 케이스라고 할 수 있다.

그렇지만, 여전히 캐릭터셋 변경이라는 것은 위험하므로, 반드시 지원을 받아야 하는 것은 필수이다.


권장 사항

1. 바로잡기의 필요성을 인식하라

잘못된 캐릭터셋을 가진 데이터베이스를 계속 사용하는 것은 결국 잘못된 애플리케이션 개발을 유도해 전체적으로 잘못된 요소들만으로 이루어진 시스템이 되고 만다. 초기 구성의 부적절함으로 인해 특히 개발자가 고생할 수 있다. 개발자의 입장에서는 KO16MSWIN949나 UTF8기반이 아닌, US7ASCII 데이터베이스를 기반으로 한글을 입출력하는 프로그램을 개발하도록 강요받는다면 결국 억울하게 부가적인 업무를 전가받는 것이다.

캐릭터셋 변경은 비단 데이터베이스 자체 뿐 아니라, 애플리케이션을 비롯한 전체 시스템에 영향을 주기 때문에 경우에 따라 단순한 작업이 아니며, 짜투리 시간을 활용하여 감행할 수 있는 사항이 아니다. 적절한 시기를 선택해야 한다.

여러분이 자바 기반의 웹 개발자라면 다음과 같은 코드를 사용하여 개발한 적이 있는가?

저장:
String p_UserComment = new String(request.getParameter("comment").getBytes("KSC5601"),"8859_1");
pstmt.setString(1,p_UserComment);
pstmt.executeUpdate();

조회:
if(resultSet.next())
{
    String v_UserComment = new String(resultSet.getString("comment").getBytes("8859_1"),"KSC5601");
..
{

이런 억지스런 인코딩의 변환이야말로 잘못된 구성으로 인해 개발자들이 부가적으로 고생하는 단적인 예이다.  한글을 쓰든, 중국어든, 베트남어 기반의 애플리케이션이든, 올바른 구성의 시스템상에서는 저런 식의 코드 변환이 필요할 리가 없다.

꼬여진 개발은 결국 가까운 미래에 "확장 불가", "마이그레이션 불가"라는 벽에 부닥칠 수 있다.

2. 절대 함부로 변경하지 말라

 여러분들 중에 함부로 Production(Live) Database를 변경할 만한 경솔함과 만용을 가진 사람이 있을 거라고 생각해서 이를 언급하는 것은 아니다. 하지만, 제아무리 실력이 뛰어난 DBA라고 할 지라도, 데이터 전체를 무력화 시킬 수 있는 작업을 홀로 감행하지 않기를 거듭 강조하고 싶다. 반드시 적어도 다음 사람들이 연관되고 협력해야 한다

  • 해당 사용업체의 DBA
  • 의사결정 권한을 가지고, 시스템 전체의 운영을 책임지는 책임자
  • 오라클의 지원 엔지니어


 

캐릭터셋을 변경 방식과 위험성

캐릭터셋 변경이란?

일반적으로 우리가 "캐릭터셋을 변경한다"고 표현하는 작업은 사실 다음 두 가지 작업을 동시에 의미한다. 따라서, 이 말이 사용될 때에는 다음 두 가지 중 어떤 의미를 가리키는지 확실히 짚고 넘어갈 필요가 있다.

변경 케이스 1) 캐릭터셋 딕셔너리 정보 변경 + 데이터 불변

캐릭터셋 변경 시도 시점을 기준으로, 데이터베이스에 저장되어 있는 데이터 자체는 전혀 변경하지 않은 채,  딕셔너리에 있는 캐릭터셋 정보만 변경하는 작업이다.  일반적으로 캐릭터셋에 맞게 데이터를 잘 저장시켜 온 데이터베이스에는 사용할 수 없는 방법이다. 예를 들어 KO16MSWIN949 캐릭터셋을 가진 데이터베이스에 한글을 Windows-949의 코드페이지에 맞게 잘 저장하고 사용해 왔다면 이 데이터베이스의 딕셔너리 정보만을 UTF8으로 바꾸어서는 전혀 엉뚱한 결과를 얻게 되는 것이다. 주로 다음과 같은 상황에서 사용할 수 있다.

  • 비어있는(empty) 데이터베이스 인스턴스 : DBCA(Database Configuration Assistants)를 이용해 데이터베이스 인스턴스를 생성할 때, 캐릭터셋을 잘못 선택했다면, 도중에 작업을 중단하는 방법도 있지만(사실 중단하기를 권장한다),  생성을 마친 후 캐릭터셋 정보를 변환함으로써 문제를 해결할 수 있는 경우가 있다. KO16MSWIN949로 생성해야 할 데이터베이스를 KO16KSC5601로 설정하여 생성했다면, 생성을 마친 후 간단한 절차를 거쳐 KO16MSWIN949로 변경할 수 있는 것이다.

  • 캐릭터셋과는 전혀 엉뚱하고도 다른 캐릭터셋으로 인코딩된 정보를 강제 저장해 온 인스턴스 : 이 경우가 상당히 많은데, 데이터베이스 캐릭터셋은 US7ASCII밖에 없는 것으로 생각을 했던 것일까? US7ASCII은 그 이름만 보아도 절대 한글을 저장할 수 없을 것 같은 캐릭터셋인데, 여기에 한글 데이터를 버젓이 저장해온 시스템들이 상당히 많다. 왜 이런 일이 발생할 수 있는가에 대해서는 지난 연재의 NLS_LANG 변수 코너를 읽어보고 생각해 보기 바란다. 캐릭터셋은 US7ASCII이지만, 실제로는 완성형 + 한글 윈도우즈 코드 페이지(KO16MSWIN949)의 데이터를 저장해 온 경우, 데이터는 현재 상태에서 가공하지 않은 채, 딕셔너리에 있는 캐릭터셋 정보만을 강제로 KO16MSWIN949로 변환하는 방식으로 문제를 해결할 수 있다.

"ALTER DATABASE CHARACTER SET" 명령어가 이에 해당하며, 여기에 대해서는 다음 섹션에서 자세한 절차를 다루도록 하겠다.

변경 케이스 2) 캐릭터셋  딕셔너리 정보 변경 + 데이터 변경

캐릭터셋의 변경과 함께 데이터베이스가 가지고 있는 데이터의 인코딩까지도 변경하는 마이그레이션 작업을 포함하는 경우로, "변경 케이스 1"에 비해 상당한 시간이 소요되는 방대한 작업이며 그에 따라 위험성이 크다. 기존 데이터베이스의 백업은 물론 당연하며, 새로운 캐릭터셋을 가진 새 데이터베이스 인스턴스를 생성하여 그곳에 현재 데이터베이스로부터 데이터를 마이그레이션을 하는 것이 가장 안전하다. 이 경우 보통 exp/imp를 사용하여 해결할 수 있다. 현존하는 데이터베이스의 데이터를 직접 변경하려고 할 경우를 대비해 오라클에서는 CSSCAN이라는 툴을 제공하고 있다.

캐릭터셋 변경의 위험성

"내가 책임지고 변경하겠소!"

일반적으로 존재하는 데이터베이스의 캐릭터셋을 변경하는 작업은 상당히 위험하다. 그래서, 현업에서는 누구도 쉽사리 총대를 매고 이 명령어를 수행하려고 들지 않는다. 괜한 용기를 부렸다가는 돌이킬 수 없는, 또는 돌이키기가 너무나 고통스러운 상황을 맞이할 수 있는 게 이런 작업이다. 

실제로 데이터베이스 캐릭터셋을 변경할 때 대략 다음과 같은 문제가 발생할 수 있다.

1) 데이터 절삭

 KO16KSC5601이나 KO16MSWIN949 캐릭터셋을 가진 데이터베이스에서는 한글 한 글자당 2바이트의 물리적 공간이 소모된다.  그래서, 대부분의 데이터베이스 스키마 설계시 한글 한 글자를 2바이트로 감안하는 경우가 많다. 예를 들어, 한글 및 영문 50자까지 허용할 수 있는 컬럼의 경우, 대략 컬럼 길이를 설정할 때 100바이트로 설정하게 된다. 이런 상황에서, 다국어 지원의 필요성이 있어, UTF8로 마이그레이션해야 하는 상황을 생각해 보자. UTF8 데이터베이스에서는 한글 한 글자당 3바이트가 소모되므로 당장 100바이트의 컬럼은 50자가 아닌 33자밖에 저장하지 못하는 컬럼으로 전락한다.

다음과 같은 상황을 생각해 보자. KSC5601데이터베이스에 길이 10바이트의 컬럼을 가진 테이블이 있다고 가정하고, 여기에 5자의 한글을 꽉 채워보자.

SQL> CREATE TABLE t (sval VARCHAR2(10), svalchar VARCHAR2(5 CHAR));
SQL> INSERT INTO t VALUES('한국오라클','한국오라클');

C:\Documents and Settings\jwryoo>set NLS_LANG=.KO16KSC5601
C:\Documents and Settings\jwryoo>exp scott/tiger@KSC5601
...
. . exporting table                              T          1 rows exported
Table(T) or Partition(T:P) to be exported: (RETURN to quit) >
...

C:\Documents and Settings\jwryoo>imp scott/tiger@AL32UTF8
. importing SCOTT's objects into SCOTT
. . importing table                            "T"
IMP-00019: row rejected due to ORACLE error 12899
IMP-00003: ORACLE error 12899 encountered
ORA-12899: value too large for column "SCOTT"."T"."SVAL" (actual: 15, maximum: 10)
Column 1 한국오라클
Column 2 한국오라클          0 rows imported
Import terminated successfully with warnings.

마이그레이션하기 전에 반드시 테이블의 컬럼 길이는 비율에 맞게 확장해야 한다.

-- 10 * 3 / 2 = 15
SQL> alter table t
  2  modify sval varchar2(15);

Table altered.

SQL> alter table t
  2  modify svalchar varchar2(15);

Table altered.

..
C:\Documents and Settings\jwryoo>set NLS_LANG=.KO16KSC5601
C:\Documents and Settings\jwryoo>exp scott/tiger@KSC5601
....
Export terminated successfully without warnings.
:\Documents and Settings\jwryoo>imp scott/tiger@AL32UTF8
Enter table(T) or partition(T:P) names. Null list means all tables for user
Enter table(T) or partition(T:P) name or . if done: T

Enter table(T) or partition(T:P) name or . if done: .

. importing SCOTT's objects into SCOTT
. . importing table                            "T"          1 rows imported
Import terminated successfully without warnings.

C:\Documents and Settings\jwryoo>sqlplus scott/tiger@AL32UTF8
SQL> SELECT * FROM t;

SVAL                           SVALCHAR
------------------------------ -----------
한국오라클                      한국오라클

US7ASCII 데이터베이스에 한글을 저장하게 될 경우, 한 글자당 거의 100% 2바이트로 저장된다. 사용자들이 일반적으로 사용하는 한글 클라이언트는 대부분 Windows-949(한글 Windows OS)나 KSC5601 완성형(UNIX계열)이고 이들은 모두 2바이트이기 때문이다. 따라서, 여기에 한글을 저장하므로 마찬가지로 "한글최대길이 * 2"의 길이만큼 컬럼을 지정하게 된다. 하지만, US7ASCII 데이터베이스를 바로잡을 때에는 KO16MSWIN949 캐릭터셋으로 강제변환하게 되므로 데이터에 대한 변경은 없다. 따라서, 이 부분만큼은 크게 문제가 없다고 하겠다. 하지만 역시 USASCII에서 UTF8으로 갈 경우에는, KO16MSWIN949로 강제 변경 후, 데이터 마이그레이션을 해야 하므로, 반드시 스키마 점검을 해야 할 것이다.

2) 데이터 깨짐

데이터 마이그레이션 시 일부 데이터 혹은 전체 데이터가 손상되는 경우가 발생한다. 그래서 특히 타겟 데이터베이스와 동일한 조건의 데이터베이스를 이용해 테스트를 충분히 하는 것은 필수 과정이다. 특히 exp/imp를 이용하여 데이터 마이그레이션을 할 때 이런 현상이 나타난다.

US7ASCII 데이터베이스의 한글 데이터를 exp/imp를 이용하여 KO16MSWIN949 또는 UTF8로 마이그레이션

데이터 전체가 손상된다. 불가능하며 시간낭비이다. 애초에 시도하지 않는 것이 좋다. 일부 DBA들이 꼼수를 사용하고 있으나, 그 위험은 시도하는 본인의 부담이라는 것을 잘 인식해야 한다(at their own risk).

KO16KSC5601 데이터베이스의 한글 데이터를 exp/imp를 이용하여 KO16MSWIN949 또는 UTF8로 마이그레이션

이 경우가 데이터 일부 깨짐 현상이 발생할 수 있는 대표적인 케이스이다. KO16KSC5601은 만능 한글 캐릭터셋이 아니라 완성형 2350자만을 지원한다는 사실은 이미 누누히 강조해 왔을 것이다. 하지만 NLS_LANG=.KO16KSC5601으로 설정함으로써, 많은 데이터베이스가 비완성형 한글 8822자들의 일부를 저장하고 사용해 왔을 것으로 믿는다. 이와 같은 데이터는 정상적인 절차를 거쳐 exp/imp를 이용해 다른 캐릭터셋의 데이터베이스로 마이그레이션이 불가능하다.

C:\Documents and Settings\jwryoo>set NLS_LANG=.KO16KSC5601

C:\Documents and Settings\jwryoo>sqlplus scott/tiger@KSC5601
SQL> select sval from t;

SVAL
---------------
커피숖

C:\Documents and Settings\jwryoo>exp scott/tiger@KSC5601
....

C:\Documents and Settings\jwryoo>imp scott/tiger@AL32UTF8
..
Enter table(T) or partition(T:P) name or . if done: .

. importing SCOTT's objects into SCOTT
. . importing table                            "T"          1 rows imported
Import terminated successfully without warnings.

SQL> sqlplus scott/tiger@AL32UTF8
SQL> select sval from t;

SVAL
------------------------------
커피?

SQL> select dump(sval) from t;

DUMP(SVAL)
-------------------------------------------------

Typ=1 Len=12: 236,187,164,237,148,188,239,191,189

-- 231 191 189 : Corrupted character  (ㅤ숖 : 236 136 150)

이런 경우는 "ALTER DATABASE" 명령어로 KO16KSC5601에서 KO16MSWIN949로 딕셔너리 변경(변경 케이스 1)만을 한 후 작업하면 문제를 피할 수 있다.

3) 애플리케이션 오동작

US7ASCII 데이터베이스에 한글을 저장하고 그것을 기반으로 만들어진 애플리케이션은 과연 캐릭터셋을 올바르게 변경한 후에 안전할 수 있을까? 정답은 물론 "없다"이다. 문자열을 처리하는 연산에서 대혼란이 올 수 있다. US7ASCII 데이터베이스에 저장된 한글의 모습을 들여다보자.

US7ASCII:



SQL> select sval from t;

SVAL
-------------------------------------

한국오라클

SQL> select length(sval) from t;

LENGTH(SVAL)
------------
          10

SQL> select instr(sval,'오') from t;

INSTR(SVAL,'오')
----------------
               5

SQL> select substr(sval,3,2) from t;

SU
--


SQL> select concat(substr(sval,3,3),'좋아') from t;

CONCAT(
-------
국옥종

KO16KSC5601:
KO16MSWIN949:
UTF8, AL32UTF8:


SQL> select sval from t;

SVAL
-------------------------------------

한국오라클

SQL> select length(sval) from t;

LENGTH(SVAL)
------------
           5

SQL> select instr(sval,'오') from t;

INSTR(SVAL,'오')
----------------
               3

SQL> select substr(sval,3,2) from t;

SUBSTR(SVAL,3,2)
----------------
오라

SQL> select concat(substr(sval,3,3),'좋아') from t;

CONCAT(SUBSTR(SVAL,3,3),'좋아')
------------------------------------
오라클좋아

예에서 보는 바와 같이 스트링 연산들이 US7ASCII에서는 왜곡될 수 밖에 없다. 따라서, 애플리케이션 개발자들도 이 왜곡된 결과를 바로잡는 코드를 부가적으로 애플리케이션에 넣도록 강요받게 된다.

US7ASCII:
왜곡된 애플리케이션 코드를 이용한 결과 바로 잡기


SQL> select sval from t;

SVAL
-------------------------------------

한국오라클

SQL> select length(sval)/2 from t;

LENGTH(SVAL)/2
------------
          5

SQL> select instr(sval,'오') from t;    -- 왜곡된 결과 그대로 사용

INSTR(SVAL,'오')
----------------
               5

SQL> select substr(sval,5,4) from t;

SU
--


SQL> select concat(substr(sval,5,6), '좋아') from t;

CONCAT(SUB
----------
오라클좋아

KO16KSC5601:
KO16MSWIN949:
UTF8, AL32UTF8:


SQL> select sval from t;

SVAL
-------------------------------------

한국오라클

SQL> select length(sval) from t;

LENGTH(SVAL)
------------
           5

SQL> select instr(sval,'오') from t;

INSTR(SVAL,'오')
----------------
               3

SQL> select substr(sval,3,2) from t;

SUBSTR(SVAL,3,2)
----------------
오라

SQL> select concat(substr(sval,3,3),'좋아') from t;

CONCAT(SUBSTR(SVAL,3,3),'좋아')
------------------------------------
오라클좋아

보는 바와 같이 원하는 결과를 얻기 위해서는 개발자가 일일이 "한글은 2바이트"라는 가정 하에 바이트 단위의 연산을 해 가며 프로그래밍을 해야 한다.

오라클을 이용해 바이트단위의 프로그래밍을 하는 것은:
- 최신식 오디오 시스템을 구축해 놓고 라디오 가능만 사용하는 것
- 전자동 세탁기를 사 놓고, 손으로 통을 돌려 세탁하는 것

- 가스렌지를 구비해 놓고  성냥으로 불을 켜는 것

대조표의 오른쪽에 있는 것처럼, 한글을 지원하는 데이터베이스의 경우 한글을 2바이트로 저장하든, 3바이트로 저장하든 스트링 연산 함수는 기본적으로 캐릭터 단위로 동작하므로 애플리케이션의 코드가 동일함을 알 수 있다. 잘못된 데이터베이스 캐릭터셋은 잘못된 애플리케이션 코딩을 유도하고, 이는 나중에 제대로 된 데이터베이스 캐릭터셋으로 마이그레이션시 심각한 휴유증을 양산할 소지가 있다. 따라서, 캐릭터셋 변경시 애플리케이션의 스트링 연산 부분을 손보는 것은 필수작업이다.

캐릭터셋 변경의 실제

ALTER DATABASE 명령을 이용한 딕셔너리 변경

앞서 살펴본 "변경 케이스 1"에 해당하는 것으로 데이터에 대한 어떠한 검토나 수정은 없이 딕셔너리의 캐릭터셋 정보만을 교체하는 작업이다. KO16KSC5601을 KO16MSWIN949로 변경할 때 유용하다. 그리고 한글만을 저장해 온 US7ASCII 데이터베이스 또한 이 방식으로 KO16MSWIN949로 변경할 수 있다.

절차

 SQL> SHUTDOWN IMMEDIATE;
<do a full backup>
SQL> STARTUP MOUNT;
SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;
SQL> ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
SQL> ALTER SYSTEM SET AQ_TM_PROCESSES=0;
SQL> ALTER DATABASE OPEN;
SQL> ALTER DATABASE CHARACTER SET KO16MSWIN949;
SQL> SHUTDOWN IMMEDIATE;
SQL> STARTUP;

SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup mount
ORACLE instance started.

Total System Global Area  171966464 bytes
Fixed Size                   777956 bytes
Variable Size             145760540 bytes
Database Buffers           25165824 bytes
Redo Buffers                 262144 bytes
Database mounted.
SQL> ALTER SYSTEM ENABLE RESTRICTED SESSION;

System altered.

SQL> ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0
  2  ;

System altered.

SQL> ALTER SYSTEM SET AQ_TM_PROCESSES=0;

System altered.

SQL> ALTER DATABASE OPEN
  2  ;

Database altered.

SQL> ALTER DATABASE CHARACTER SET KO16MSWIN949;

Database altered.

-- 한편 현재 캐릭터셋의 Superset이 아닌 캐릭터셋으로는 변경이 불가능하다.

SQL> ALTER DATABASE CHARACTER SET WE8DEC;
ALTER DATABASE CHARACTER SET WE8DEC
*
ERROR at line 1:
ORA-12712: new character set must be a superset of old character set

-- 다시 데이터베이스를 재구동하면 이제 어엿한 KO16MSWIN949 데이터베이스가 되어 있다.

C:\Documents and Settings\jwryoo>set NLS_LANG=.KO16MSWIN949

C:\Documents and Settings\jwryoo>sqlplus scott/tiger@KSC5601

SQL> select sval from t;

SVAL
---------------
커피숖 

변경 후에는 이제 exp/imp를 이용하여 UTF8 데이터베이스로 마이그레이션하는 것도 가능해진다.

이 방식을 사용하려면 반드시 새로운 캐릭터셋은 기존 캐릭터셋의 Superset이어야 한다. 그렇지 않으면 기존 데이터의 안전이 보장될 수 없기 때문이다. 

기존 캐릭터셋
새로운 캐릭터셋
변경 가능 여부
US7ASCII
KO16KSC5601/KO16MSWIN949/UTF8/AL32UTF8
가능
KO16KSC5601
KO16MSWIN949
가능
KO16MSWIN949
UTF8
불가능
UTF8
AL32UTF8
가능

강제로 캐릭터셋을 변경하는 옵션 또한 알려져 있으나, 이 지면에 소개할 수 없는 점 양해 바란다. 간단하지만 극히 위험한 작업이다. 이미 강조했지만, 반드시 오라클 엔지니어나 오라클과 지원계약을 맺은 고객의 담당자가 충분히 그 위험성과 방법에 대해 숙지한 후에 작업할 수 있따는 것을 명심하기 바란다.

데이터 마이그레이션

준비작업

  • 데이터베이스 컬럼 길이 점검 : 데이터 자체가 변경되므로 데이터의 길이 또한 변경될 수 있다.
  • 애플리케이션 점검 : 애플리케이션 또한 스키마 및 캐릭터셋 변경으로 인해 영향을 받지 않는지 점검한다. 특히 UTF8으로 갈 경우, 한글을 2바이트로 가정하고 작성한 코드가 없는지 점검한다.

CSSCAN 실행

CSSCAN은 캐릭터셋 변경시 발생할 수 있는 문제점을 미리 감지하고 보고서를 생성해준다는 점에서 매우 유용하다. 비록 이것을 실행하는 것이 완전히 필수적인 요소라고 할 수는 없어도(CSALTER 방식에서는 필수, 지난 일주일 내에 CSSCAN을 수행한 결과가 있어야 함) 지금 보유하고 있는 데이터가 소중하다고 생각된다면 반드시 돌려보는 것이 좋을 것이다.

1) CSSCAN 설치

SQL> connect / as sysdba
Connected.
SQL> @/home/oracle/oracle/rdbms/admin/csminst.sql

2) CSSCAN 구동

[oracle@krrnddel oracle]$ csscan system/..

Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options

(1)Full database, (2)User, (3)Table: 1 > 2

Current database character set is KO16KSC5601.

Enter new database character set name: > KO16MSWIN949

Enter array fetch buffer size: 102400 >

Enter number of scan processes to utilize(1..32): 1 >

Enter user name to scan: > SCOTT

Enumerating tables to scan...

. process 1 scanning SCOTT.DEPT[AAAL+oAAEAAAAAJAAA]
. process 1 scanning SCOTT.EMP[AAAL+qAAEAAAAAZAAA]
. process 1 scanning SCOTT.BONUS[AAAL+sAAEAAAAApAAA]
. process 1 scanning SCOTT.TESTTBL[AAAME1AAEAAAAA5AAA]
. process 1 scanning SCOTT.KANGYS[AAAME5AAEAAAABBAAA]
. process 1 scanning SCOTT.DEPTENTITY_GRADUATE_DEPTCOMP[AAAMI8AAEAAAABJAAA]
. process 1 scanning SCOTT.DEPTENTITY_DEPT_DEPTCOMP[AAAMJAAAEAAAAB5AAA]
. process 1 scanning SCOTT.NLSTECH_SAMPLE_SORT_KOREAN_M[AAAMm8AAEAAAABZAAA]
. process 1 scanning SCOTT.NLSTECH_SAMPLE_SORT_KSC5601[AAAMm9AAEAAAACBAAA]
. process 1 scanning SCOTT.CHARSET_TEST[AAAMwLAAEAAAACJAAA]
. process 1 scanning SCOTT.T[AAAM9IAAEAAAACRAAA]

Creating Database Scan Summary Report...

Creating Individual Exception Report...

Scanner terminated successfully.

$ ls scan*
scan.err  scan.out  scan.txt

  • scan.out : STDOUT에 출력된 결과를 저장해 놓은 파일
  • scan.err : 캐릭터셋 변경시 손실되는 데이터

    scan.err의 내용물 예:

    [Application data individual exceptions]

    User  : SCOTT
    Table : TESTTBL
    Column: VAL
    Type  : VARCHAR2(100)
    Number of Exceptions         : 1
    Max Post Conversion Data Size: 6

    ROWID              Exception Type      Size Cell Data(first 30 bytes)
    ------------------ ------------------ ----- ------------------------------
    AAAME1AAEAAAAA9AAA lossy conversion         <8c>c
    ------------------ ------------------ ----- ------------------------------
    ...

  • scan.txt : 종합 보고서. 다음 예에 나온 섹션들이 가장 핵심적인 부분이라 하겠다. Data Dictionary 분석 테이블이 비어 있는 것은위의 CSCSAN 실행을 User단위로(SCOTT 사용자에 한해) 테스트를 했기 때문이다. 새로운 캐릭터셋에서도 변경이 없는 데이터(Changeless, 주로 아스키 영문 데이터)와 변경 가능한(Convertible) 데이터들의 합이 정상적으로 마이그레이션되는 데이터의 비중을 보여준다.
    • Truncation : 변경 후 Truncate될 데이터
      Lossy : 변경 후 손상될(깨질) 데이터

    ...
    [Data Dictionary Conversion Summary]

    Datatype                    Changeless      Convertible       Truncation            Lossy
    --------------------- ---------------- ---------------- ---------------- ----------------
    VARCHAR2                             0                0                0                0
    CHAR                                 0                0                0                0
    LONG                                 0                0                0                0
    CLOB                                 0                0                0                0
    VARRAY                               0                0                0                0
    --------------------- ---------------- ---------------- ---------------- ----------------
    Total                                0                0                0                0
    Total in percentage              0.000%           0.000%           0.000%           0.000%


    [Application Data Conversion Summary]

    Datatype                    Changeless      Convertible       Truncation            Lossy
    --------------------- ---------------- ---------------- ---------------- ----------------
    VARCHAR2                            37               20                1                5
    CHAR                                 0                0                0                0
    LONG                                 0                0                0                0
    CLOB                                 4                0                0                0
    VARRAY                               0                0                0                0
    --------------------- ---------------- ---------------- ---------------- ----------------
    Total                               41               20                1                5
    Total in percentage             61.194%          29.851%           1.493%           7.463%

    [Distribution of Convertible, Truncated and Lossy Data by Table]

    USER.TABLE                                              Convertible       Truncation            Lossy
    -------------------------------------------------- ---------------- ---------------- ----------------
    SCOTT.CHARSET_TEST                                                0                0                3
    SCOTT.DEPTENTITY_DEPT_DEPTCOMP                                    2                1                0
    SCOTT.NLSTECH_SAMPLE_SORT_KOREAN_M                                5                0                0
    SCOTT.NLSTECH_SAMPLE_SORT_KSC5601                                 5                0                0
    SCOTT.T                                                           0                0                1
    SCOTT.TESTTBL                                                     8                0                1
    -------------------------------------------------- ---------------- ---------------- ----------------

    [Distribution of Convertible, Truncated and Lossy Data by Column]

    USER.TABLE|COLUMN                                       Convertible       Truncation            Lossy
    -------------------------------------------------- ---------------- ---------------- ----------------
    SCOTT.CHARSET_TEST|CHARCOL                                        0                0                3
    SCOTT.DEPTENTITY_DEPT_DEPTCOMP|DNAME                              2                1                0
    SCOTT.NLSTECH_SAMPLE_SORT_KOREAN_M|TEXT                           5                0                0
    SCOTT.NLSTECH_SAMPLE_SORT_KSC5601|TEXT                            5                0                0
    SCOTT.T|SVAL                                                      0                0                1
    SCOTT.TESTTBL|VAL                                                 8                0                1
    -------------------------------------------------- ---------------- ---------------- ----------------

    [Indexes to be Rebuilt] : 캐릭터셋 변경 후 재생성되어야 하는 인덱스들의 리스트

    USER.INDEX on USER.TABLE(COLUMN)                                                         
    -----------------------------------------------------------------------------------------
    -----------------------------------------------------------------------------------------


 이 결과를 보면 어떤 테이블의 어떤 행에서 어떤 컬럼에 어떤 데이터가 손상이 될 수 있는지 살펴볼 수 있다. 단 US7ASCII 데이터베이스에 한글을 저장해 놓고 KO16MSWIN949 또는 UTF8로 데이터 마이그레이션 및 캐릭터셋 변환이 가능한지 CSSCAN을 돌려보고 싶다면 그러지 않기를 권장한다. 그것은 불가능하다는 것은 명백하고 잘못하면 엄청난 크기의 리포트만을 하염없이 기다려야 할 지도 모른다.

그런데 데이터 딕셔너리에는 Changeless만 있는 것이 사실 가장 안전하다. 변경 가능한(Convertible) 데이터가 존재하는 것이 100% 나쁘다고 할 수는 없겠지만, 딕셔너리가 수정되었을 때의 파장은 알 수 없으므로 이는 조심해야 한다.

  • CLOB에 저장된 데이터 : US7ASCII에서는 CLOB에 있는 데이터 역시 US7ASCII 형식으로 저장되지만, KO16MSWIn949나 UTF8같은 멀티바이트 캐릭터셋에서는 데이터가 UCS-2와 호환되는 고정 바이트로 CLOB에 저장된다. 따라서, US7ASCII에서 다른 멀티바이트 캐릭터셋으로 마이그레이션하려고 CSSCAN을 구동했을 때에는 CLOB내의 데이터는 모두 Changeless가 아니라 Convertible로 처리될 것이다.
  • 당연한 이야기이지만, 캐릭터들은 기존 캐릭터셋과 새로운 캐릭터셋에 모두 존재하지만 실제 바이너리값에서는 차이가 있다. KO16MSWIN949에서의 "가"(176 160)와 UTF8에서의 "가"(234 176 128)는 엄연히 바이너리 값이 다르다. 따라서, 멀티바이트(한글)로 테이블명이나 인덱스명을 생성했을 때 CSSCAN은 이들을 Convertible로 처리할 것이다.

exp/imp를 사용하여 새로운 데이터베이스로 데이터와 딕셔너리를 마이그레이션할 것이 아니라면(CSALTER를 사용할 것이라면), 오라클은 안전을 위해 CLOB에 들어있지 않은 딕셔너리 Convertible 데이터들을 마이그레이션 전에 처리할 것을 권장한다

  • 한글 이름으로 된 테이블 이름 변경
  • 한글 이름으로 된 테이블 및 객체들은 exp후 drop시키고, 마이그레이션 후 다시 imp한다

기타 딕셔너리에서 Lossy나 Corrupted된 데이터가 발생한다면 이는 심각하며 반드시 전문가나 오라클 기술 지원 인력과 상의해야 할 문제이다.

exp/imp를 이용한 변경

전통적인 백업 방식을 이용하여 마이그레이션하는 방법으로 다른 버전의 데이터베이스들끼리의 데이터 마이그레이션 작업을 상당히 깔끔하게 할 수 있다는 장점이 있다. 하지만, exp가 잘되었다거나 imp가 잘 되었다는 것이 데이터가 안전하게 저장되었다는 것을 검증해 주지는 못한다. 예를 들어 KO16KSC5601 데이터베이스에서 exp한 파일에 잘못된 데이터(숖)이 있다고 하더라도 이것을 다시 UTF8 데이터베이스에 imp했을 때 그것을 발견하지 못한다. exp/imp에 대해서는 앞서 충분히 보여주었으므로 따로 예제를 들지 않겠다. NLS_LANG 변수값의 설정이 매우 중요하다는 사실은 확실히 기억하기 바란다.

CSALTER를 이용한 변경

실제 CSALTER를 수행하기 전에 반드시 Full Database Scan이 이루어져야 한다. 그렇지 않으면 다음과 같은 메시지를 만나게 될 것이다. 

SQL> @@/home/oracle/oracle/rdbms/admin/csalter.plb normal

0 rows created.


Function created.


Function created.


Procedure created.

This script will update the content of the Oracle Data Dictionary.
Please ensure you have a full backup before initiating this procedure.
Would you like to proceed ?(Y/N)?y 
..
Full database scan is required

csalter.plb는 SYS사용자만이 수행할 수 있고, 수행 시점에서 이 데이터베이스에 접속된 유일한 세션이어야 한다. 그리고 반드시 새로운 캐릭터셋은 기존 캐릭터셋의 Superset이어야 한다. 한글 지원 캐릭터셋과 US7ASCII 캐릭터셋들의 Subset-Superset 관계는 앞서 ALTER DATABASE 방식을 설명하는 중 소개된 
테이블을 참조하기 바란다.

글을 마치며

캐릭터셋 변경은 필요하고도 위험하다. 캐릭터셋을 잘못 사용해 왔다는 사실을 알게 되는 시점부터 운영자는 고민에 빠진다. 캐릭터셋을 잘못 사용해 왔다는 것은 그 상단의 애플리케이션조차도 잘못되어 있을 가능성이 높다는 의미이다. 따라서, 데이터를 잘 보존하면서 캐릭터셋을 옮기는 것도 리스크가 크지만, 그 상단의 애플리케이션 또한 다시 디버깅하고 테스팅해야 한다는 사실 또한 운영자에게 두려움으로 다가올 수 있다.

  • 캐릭터셋 변경에 대한 방식을 충분히 습득하자
  • 캐릭터셋 변경에 따른 위험성을 충분히 이해하자
  • 캐릭터셋 변경에 대한 관계자들의 폭넓은 동의가 확보하자
  • 캐릭터셋 변경 전에 반드시 변경을 되돌릴 수 있는 장치를 마련하자
  • 캐릭터셋 변경 전에 충분히 테스트 DB로 테스트하자 

 

Posted by redkite
, |

오라클과 NLS의 찰떡궁합 들여다보기 

  • 오라클의 NLS 지원 특성
    1. 영역(Territory)별 지원
    2. 언어(Language)적 지원
  • 오라클 제품 처음부터 올바르게 설치하자
  • 올바른 캐릭터셋을 선택하자
    1. KO16KSC5601
    2. KO16MSWIN949
    3. UTF8/AL32UTF8
    4. National Characterset
    5. 캐릭터셋 선택의 원칙
  • 올바른 NLS 환경변수값 설정하기
    1. NLS_LANG
      1. 1) NLS_LANG 변수의 구성
      2. 2) NLS_LANG 변수값 설정의 기본 원칙
      3. 3) 데이터베이스의 캐릭터셋과 동일한 값으로 캐릭터셋을 설정하는 경우
  • KO16KSC5601에 서 지원되지 않는 글자들을 KO16KSC5601 데이터베이스에 입출력하기
  • 오라클 데이터베이스에서의 한글 정렬
    1. KO16KSC5601 데이터베이스
    2. UTF8/AL32UTF8 데이터베이스
      1. NLS_SORT=’KOREAN_M’ 이용
    3. KO16MSWIN949 데이터베이스
      1. 방법 1) NLS_SORT=’UNICODE_BINARY’ 이용
      2. 방법 2) NLS_SORT=’KOREAN_M’ 이용
    4. 인덱스를 이용하여 성능 향상시키기
  • 오라클 데이터베이스에서 한글 비교하기
    1. NLS_COMP
  • 글을 마치며 


    Oracle Forms를 이용하여 기업용 어플리케이션을 개발할 때 요구되는 화면의 유형은 크게 두 가지로 나눌 수 있다.
    하나는 입력용 화면이고 또 다른 유형은 조회용 화면이다. 입력용 화면은 데이터의 정합성 에 초점을 두어 개발이 이루어지며, 조회용 화면은 다양한 조건으로 데이터를 조회할 수 있는 기능에 초점을 맞추고 있다.

    "왜 우리만 한글이 제대로 안 보이는 거야. 이거 비싼 돈 주고 도입했더니 완전 엉터리 아냐? 
    아니면 오라클 개발자들이 꼬부랑말 쓰는 녀석들이라는데...영 아니올시다인가?"

    "그러니까 외국 제품들 쓰면 다 이 모양이라니깐.. 대한민국에 관심이나 있겠어?"

    어떻게든 되게 만들어야 합니다. 어김없이 사건 현장에는 혈투를 벌이는 병사가 구원을 요청한다. "한글, 한글이 왜 깨지냐고요? 
    업그레이드하기 전까지는 잘 돌아갔는데 이게 무슨 경우입니까? 고객에게 어떤 답변을 해 주어야 합니까?" 
    제 아무리 자식을 좋은 학교에 보내어도 노심초사 자식을 걱정하는 부모의 마음처럼, 천하를 호령하고 다닌다는 오라클 데이터베이스에 데이터를 저장하고 있으면서도 고객 또한 자식같은 데이터를 잃을까 노심초사하기 마련이다.  오라클의 최전방의 병사들은 이런 고객의 마음을 헤아리고자 백방으로 문제를 해결하려고 뛰어다니면서도 머릿속에 이런 생각이 든다. 
    "아, 왜 세상엔 다양한 언어들이 있어서..."

    최전방 공격수가 아니라 다국적 소프트웨어로서의 오라클 제품의 완벽성을 위해 일하고 있는 본인의 입장에서는 처음부터 
    병사들의 절규들을 이해할 수 있었던 것은 아니었다. 그도 그럴 것이 많은 문제들이 소프트웨어 자체의 결함보다는 잘못된 설정과 이해에서 비롯된 것이기 때문이었다.  하지만, "소프트웨어의 글로벌화"라는 슬로건이 제대로 등장하기 시작한지는 실질적으로 아직 10년도 되지 않았다. 게다가 다국어 지원의 개념이 직접 소프트웨어의 설계에 적용되고 개발, 출시되고 사용되기까지는 그 10년 중 상당 부분이 소모되어야 했다. 그러므로 아직 시스템의 "한글화", "다국어화"라는 것이 100% 
    이해되기에는 무리가 있는 것이다. 

    "지금부터라도 제대로 "글로벌화"된 시스템을 이해하고 적용해 보는 거야!" 

    오라클 데이터베이스가 속편하게 입력되는 데이터를 마치 바이너리 스트림을 저장하듯이 텍스트 데이터를 언어의 특성에 맞는지 검사하거나 변환하는 작없없이 저장한다는 오히려 겉으로 드러나는 문제점이 더 적게 보이는 착시 현상이 발생할 지도 모른다. 
    또한 오라클 데이터베이스가 현재 사용자에 대해, 어느 나라에 있는지 고려하지 않고 단순히 날짜나 숫자 데이터를 저장만 해주고, 어떤 형식으로 날짜가 출력되어야 사용자가 이해할 수 있는지, 어떤 통화기호를 사용해야 사용자가 제대로 된 값을 이해할 수 있는지 고려하지 않는다면 오라클 데이터베이스 입장에서도 개발의 부담이 훨씬 적었을 것이다. 그 모든 것은 상위 시스템 개발자의 책임이니까......

    "오라클 데이터베이스 한국어판 나왔습니까?" 
    오라클의 제품들은 현재 세계 140개 이상의 다양한 로케일을 지원하고 있다. 물론 한국어도 포함한다. 단순히 한국어를 포함하는 게 아니라 한국어는 "가장 중요한 10개 언어들 중 하나"의 위치를 차지하고 있다. 그런데도 "오라클 데이터베이스의 한국어판 출시 임박" 이런 광고를 본 적이 없을 것이다. 한국어 뿐 아니라 저 100개가 넘는 모든 로케일에 대해 "~~판"이라는 것은 애시당초 존재하지 않는다. 
    왜? 오라클 제품 자체가 "글로벌판"이기 때문이다. 출시 자체를 모든 로케일에 대한 지원이 끝난 이후에야 하게 되며, 출시된 제품에는 이미 모든 로케일에 대한 지원이 포함되어 있는 것이다. 많은 제품들이 "영어 버전"의 출시 후 수 주, 혹은 수 달 후에 "한국어판"이라는 것을 출시하는 것과는 다른 전략이다. 소프트웨어의 하부 설계 자체가 이미 다국어 지원을 고려하여 만들어졌기 때문에 가능한 것이다.

    SQL> alter session set NLS_CALENDAR = 'ROC Official';

    세션이 변경되었습니다.

    SQL> SELECT sysdate from dual;

    SYSDATE
    ------------------------------------------
    中華民國94年07月21日
    [오라클은 추가적으로 6개의 달력 시스템을 지원한다]

    위에서 보듯, 여러분이 설치한 오라클 데이터베이스는 일반적으로 사용하는 Gregorian 달력 이외에 6가지 달력을 더 지원하고 있다. 
    이런 것이 오라클 소프트웨어만의 특징은 아닐 지라도 멋있지 않은가? "글로벌화"된 소프트웨어라는 것.

    "그래서 어렵다" 
    이것은 오라클 제품의 강점이기도 하지만, 그래서 사람들에게 "어렵다"는 느낌을 심어준다. 왜냐하면 오라클 제품을 사용하는 사람은 결국 입맛에 맞게 설정이 끝나 있는 "한국어판"을 사용하는 것이 아니라, "글로벌판"을 입맛에 맞게 설정하여 사용해야 하기 때문이다. 
    하지만, 그건 단순한 이유일 뿐이다. 사실은 그 "입맛에 맞는 설정"이라는 것을 제대로 할 수 있도록 해 주는 "교본"이 없다는 것이 문제이다. 그리하여 많은 DBA들조차 초기 설정을 제대로 하지 않은 채 시스템을 도입하게 되고, 무엇이든 해 낸다(자랑스럽기도 하고 개발자로서 슬프기도 한)는 실력을 갖추고 있는 우리의 개발자들이 잘못된 설정을 가진 시스템을 기반으로 하여, 어떤 수를 써서라도 문제가 발생하지 않도록 밤을 새는 것이다. 그리 어렵지 않은 "다국어 지원"을 상식화하면, 개발자들도 억울하게 고생하지 않고 작업을 빨리 마친 후 집에 가서 재미있는 드라마도 볼 수 있는 것이다.

    "알아보세. 느껴보세. 기뻐해보세"
    누구나 잘 알고 있는 것 같으면서도, 누구나 잘못 사용할 수도 있는 오라클 데이터베이스의 견고한 NLS(National Language Support) 아키텍처를 어떻게 하면 문제없이 잘 사용할 수 있는지 일단 한 번 그 세계로 빠져 보기로 하자.  약 보름 간격으로 네 번에 걸쳐 오라클의 NLS 세계를 관광하고, 관광 다녀와서 이제는 개발자도 삼순이 같은 재미있는 드라마 보러 자신있게 퇴근하는 데 조금이라도 도움이 될 만한 지식 혹은 상식을 가져 보기로 하자.

      연재 순서 
      1회 : 오라클과 NLS의 찰떡궁합 들여다보기(1)
      2회 : 오라클과 NLS의 찰떡궁합 들여다보기(2)
      3회 : 오라클 GDK를 사용하여 깔끔한 다국어 개발 유틸리티를 만들자
      4회 : 한글화된 오라클 제품, 그 이면의 비밀.

    PartⅠ. 오라클의 NLS 지원 특성.

    1. 영역(Territory)별 지원

      영국과 미국은 "영어"를 모국어로 사용하는 나라들이지만, 이들 나라에서 사용되는 날짜 표기 방법은 각각 다르다. 영국에서는 "일/월/연도"로 표기하는 반면, 미국에서는 "월/일/연도"로 표기한다. 물론 사용하는 통화기호 또한 "파운드"와 "달러"로 각각 다르다. 이렇듯, 같은 언어를 사용하는 지역이라고 해도 서로 다른 지리적, 사회적 특성으로 말미암아 서로 차이점을 가지게 마련이다.  영역 정보들이야말로, 여러분들이 NLS에 대해 "캐릭터셋 지원"이나 "번역"이상의 그 무엇으로 인식하는 데 큰 도움을 줄  것이다. 한 영역은 다음 표와 같은 고유 정보들을 포함할 수 있다.

      • 달력 설정 방법 : 어떤 나라는 한 주의 첫 번 째 요일을 일요일로 생각하고(한국), 다른 나라들 중에서는 월요일(체코)로 생각할 수 있다. 또한 한 달의 첫 번 째 주를 생각할 때, 어떤 나라(체코)는 그 달의 날짜들이 해당 주의 과반수 이상을 차지하고 있을 때 그 주를 첫 번 째 주로 생각하고, 어떤 나라(한국)에서는 최초의 완전한 한 주를 그 달의 첫 번 째 달로 생각한다.
      • 날짜 포맷 : 같은 날짜를 표기하는 데 각 지역마다 고유의 방식이 있음. 각 지역마다 "짧은 형식"과 "긴 날짜 형식"을 지정할 수 있다. 2005년 8월 10일이라는 날짜 데이터를, 한국에서는 "05/08/10 오후 07:28:03"로, 체코에서는 "10.08.05 19:28:03"로 제공할 수 있다.
      • 통화 기호 : 각 지역마다 통화기호와 금액 표기 방식이 다르다.  또한 통화의 변경(EURO화 변경과 같이)으로 인해 두 개의 통화가 사용될 수 있고(DUAL CURRENCY), 국제적으로 통용되는 ISO 통화기호도 있다. 한국은 ₩, 체코는 Kč를 사용한다.
      • 숫자 그룹 : 소수점 기호나 숫자를 그룹핑하는 방법이 지역마다 다르다. 기타 측정방식(미터방식 등)이나 반올림 방식, 음수기호의 위치 등이 지역마다 다르다. 한국에서는 소수점기호는 dot, 그룹기호는 comma이지만, 체코에서는 소수점기호가 comma, 그룹기호는 dot이다.

    2. 언어(Language)적 지원

      언어별로 달리 지원을 하는 특성은 다음과 같은 것이 있다.

      • 캐릭터셋 : 각 언어가 저장될 수 있는 캐릭터셋을 대부분 지원한다. 한국어의 경우 KO16MSWIN949와 KO16KSC5601이 있다.
      • 정렬 방식 : 각 언어별로 정렬하는 규칙이 다르다. 기본적으로는 이진 바이너리 코드값을 이용해 데이터를 정렬할 수 있지만, 
        때에 따라서는 각 언어가 가진 글자들의 고유한 특성에 맞게 정렬을 해 줄 필요성이 있다.  이런 정렬을 Linguistic Sorting이라고 한다. 한국어에 대해서는 KOREAN_M이라는 정렬 방식을 지원한다.
      • 날짜 표기에 사용되는 기호 : 날짜를 표시할 때 사용하는 month, day, day of week, year같은 정보를 그 나라에 맞게 번역하여 제공한다.
      • 에러메시지 및 UI 번역 : 사용자들의 불편을 최소화하기 위해 각 언어별로 번역된 에러 메시지와 사용자 인터페이스를 
        제공한다.

    PartⅡ. 오라클 제품 처음부터 올바르게 설치하자.

      한국어 환경을 제대로 지원하려면 그림과 같이 반드시 "한국어" "실행 언어"에 포함시켜야 한다. 사실 많은 DBA들이 이 부분을 간과하고 있다. 아무 생각 없이 계속 "다음(N)" 버튼만 누르다가는 돌이킬 수 없는 결과를 얻게 될 수 있으므로 이 과정은 정말 중요하다고 하겠다.

      언어 선택의 의미. 

      "한국어"를 선택하지 않는다고 해서 한국어를 데이터베이스에 저장할 수 없다는 것은 아니다. 
      먼저 이 "언어 선택"에서 한국어를 선택한다는 것의 의미를 정확히 알 필요가 있다. 위에 강조한 대로 한국어를 비록 선택하지 않았다고 해서 데이터베이스에 한글 데이터를 넣을 수 없다는 것은 결코 아니다. 

      "한국어 저장 여부는 오로지 캐릭터셋이 무엇인가에 달렸다"

      한국어를 제대로 저장하는가 여부는 오로지 다음에 언급할 캐릭터셋 설정에만 의존할 뿐, 실상"언어 선택"에서의 한국어 선택 여부는 관계없다는 점을 잘 구분해야 한다 위의 그림에서 묻는 바와 같이 이 화면은 "실행 환경"에서 어떤 언어를 지원할 것인가를 묻고 있다. 즉 여기에서 "한국어"를 선택하면 다음과 같은 리소스가 설치된다. 

      • 번역된 메시지 : 오라클 데이터베이스와 함께 제공되는 애플리케이션 중, iSQL*plus나 자바나 ADF 기반의 웹 애플리케이션의 경우에는 "언어 선택"과 관계없이 번역된 작업 환경이 제공된다. 하지만, SQL*Plus와 같은 기존 애플리케이션은 오라클의 번역 메시지 리소스에 의존하며 이들 파일은 각 언어별로 따로 제공된다. 만일 "한국어"를 선택하지 않으면 한국어 메시지 파일은 설치되지 않게 된다.
      • 폰 트 : 오라클 ADF(UIX 혹은 CABO) 기반의 애플리케이션의 경우, "확인", "취소" 등의 버튼이 이미지로 제공되는 경우가 많다. 이런 이미지들은 번역된 메시지를 바탕으로 UIX 엔진에 의해 동적으로 생성된다. 이 이미지가 제대로 생성되기 위해서는 반드시 특정 폰트를 필요로 하게 되는데, 이 폰트는 "한국어"를 선택하지 않을 경우 설치되지 않는다. 이 특정 폰트는 한국어, 중국어(간체, 번체) 그리고 일본어에 대해 각각 제공되므로,  만일 한국어 이외에 이들 언어도 지원해야 할 경우 필수적으로 그 언어들을 선택해야 할 것이다.

      • 로케일 정보 : 번역 뿐만 아니라 오라클 데이터베이스가 다양한 로케일 정보를 가진 클라이언트들과 제대로 통신하기 위해서는 각 국가별, 언어별로 특색있는 로케일 정보를 지니고 있어야 한다. 이들은 날짜 형식("2050-04-14" "Jul 9, 2005" 등), 통화 코드($) 등의 정보를 포함하고 있다. 한국어에 관한 로케일 정보를 위해 반드시 "한국어"를 선택해야 한다.


      한글 Windows 혹은 LANG=ko로 설정한 유닉스 환경
      이 환경에서는 그림과 같이 OUI가 한글로 뜨게 된다. 

      기타 유닉스 환경 
      DBA 중에서는 "한글" 환경에서 오라클 제품을 설치하면 오동작하거나 설치되지 않는 경우가 많다고 믿는 사람들이 종종 있다. 하지만 이것은 틀린 이야기다. 오히려 한글 데이터나 한글 기반의 애플리케이션을 위해서는 한글 환경에서 오라클 제품을 설치하는 것이 실수를 줄이는 길이다.

      "저희는 LANG=ko로 하면 깨진 화면이 떠서요"

      하지만, 아직 리눅스 등 많은 OS에서 오라클이 제공하는 번역된 인스톨 환경을 사용할 수 없다. 오라클의 인스톨러인 OUI(Oracle Universal Installer)는 자바 기반의 애플리케이션이며, 한글 출력 가능 여부는 JRE(Java Runtime Engine)와 OS에 의존한다. Sun에서 제공하는 JRE의 Linux 버전은 현 시점까지는 오로지 일본어 폰트 정보(font.properties.ja)만 제공하고 있으며 한국어에 대해서는 제공하고 있지 않다. 따라서, 부득이하게 한글이 깨져서 나오게 되는 것이다. 이 경우에는 할 수 없이 LANG=C로 설정하고 영문 환경에서 오라클 제품을 설치해야 한다. 독자들은 이제 이런 상황에서 무엇을 조심해야 하는지를 깨달았을 것이다. 이 경우 "언 어 선택"에서는 오로지 "영어"만이 선택되어 있으므로, 반드시 "한국어"를 선택하고 넘어가야 할 것이다.

    PartⅢ. 올바른 캐릭터 셋을 선택하자.

      "올바른 캐릭터셋이라 함은 한글을 저장할 수 있는 캐릭터셋을 말한다." 
      자, 설치에서 한숨을 돌렸다면, 이제 실제 데이터베이스 인스턴스가 생성될 때, 올바른 캐릭터셋을 선택하는 것이 중요하다. 
      물론 이 사항은 오라클 많은 소프트웨어 제품들 중 데이터베이스를 설치할 때에만 해당되는 사항이다. 
      캐릭터셋은 잘못 설치되었을 경우에는 그야말로 치명적이다. 이런 데이터베이스에 어떤 잘못된 방식으로든 한글 바이트 코드를 저장하고 사용하게 된다면, 돌이킬 수 없는 결과를 낳게 된다. 

      "정해진 캐릭터셋을 가지고 있지 않은 데이터베이스에 결코 한글 데이터를 저장할 수 없다."
      독자 중에는 이 말을 믿으려고 하지 않는 사람들이 있을 것이다. "설마? 내가 해 봤는데, 되던데요? 이런 생각을 가진 사람들이 있을 것이라고 믿는다. 하지만, 여러분들이 저장한 것은 결코 한글이 아니다(한글을 저장하고 사용해 왔다고 믿고 싶을 테지만). 
      여러분들이 저장해 온 것은 그저 한글을 가장한 이진 코드의 덩어리일 뿐이다. 데이터베이스가 여러분들이 던져주는 코드를 올바른 텍스트로 인지하는 능력을 억제시킨 채, 강제로 데이터를 저장하며 그것을 가지고 한글을 제대로 저장했다고 우길 수는 없는 것이다. 다 같이 믿자. 저 말은 부정할 수 없는 사실이며 결코 부정될 수 없다. 

      "왜 유독 한국 사람들은 그동안 US7ASCII 캐릭터셋을 사랑해 왔나? 이제 헤어질 때가 왔다" 
      현재 한글을 지원하는 캐릭터셋으로는 다음 네 가지가 있다. 오직 이 네 가지이다. 각각의 특색이 다르므로 유의해야 한다.

      • KO16KSC5601
      • KO16MSWIN949
      • UTF8
      • AL32UTF8

    1. KO16KSC5601

    이름에서 알 수 있는 바와 같이 이 캐릭터셋은 한글 완성형 코드와 일치한다. 완성형은 일반적으로 많이 사용되는 2350자의 한글을 25*94 매트릭스에 배열한 문자셋이며, 4888자의 한자와 히라카나, 카타카나, 그리고 영문 및 각종 기호들을 포함하고 있다. 유닉스 환경에서는 LANG=ko로 하여 DBCA(Database Configuration Assistant)를 실행할 경우, 자동으로 캐릭터셋을 KO16KSC5601로 지정한다. 물론 변경할 수도 있다.

    "KO16KSC5601 캐릭터셋을 사용하기 전에 그 특성을 제대로 알고 사용하자"

    햏햏

    모두에게 하게 되었소. 솔가 예약했던 커피이 배신을 때리는 바람에 그만 낙동강 오리알이 되었지요. 정말 하오.


    방각하

    불행하게도 여러분의 KO16KSC5601 데이터베이스는 이 글을 제대로 저장할 수 없다. 굵게 표시된 글자는 완성형에 포함되지 않은 글자들이다. 그래서 이 글을 저장한 후 다시 읽으려고 하면 다음과 같은 결과를 보게 될 뿐이다.

    ? ?

    모두에게 ?하게 되었소. 솔?가 예약했던 커피?이 배신을 때리는 바람에 그만 낙동강 오리알이 되었지요. 정말 ?하오.

    ?방각하

    비록 이런 글을 올리는 게 올바른 한국어 문화에 이바지하는 길이 아니라는 것을 잘 알고 있지만, 시스템을 개발하는 사람이 사용자에게 문화 계몽을 시킬 수는 없는 노릇이다. 또한 사람의 이름을 "솔믜"라고 짓지 말라고 요구할 수도 없다. 실제로 이쁜 이름이 아닌가?

    "캐릭터셋 지정 전에 반드시 사용할 시스템이 어느 정도 범위의 한글 지원을 원하는가를 확실히 검사할 필요가 있다"

    2. KO16MSWIN949

    Windows-949 캐릭터셋은 마이크로소프트사의 Windows Codepage 949번, 즉 한글 코드 페이지를 따른 코드셋이다. 
    이는 완성형(KO16KSC5601)을 그대로 포함하고 있으며, 추가로 현대 한글 조합으로 표현할 수 있는 모든 가짓수에 해당하는 8822자의 한글을 추가해 포함하고 있다. 그러니까 "Windows-949 캐릭터셋은 KSC5601의 수퍼셋(Superset)"이 되며, 따라서 "KO16MSWIN949 또한 KO16KSC5601의 수퍼셋"이 된다.

    "다른 운영 체제에서도 사용할 수 있다!"
    운영 체제가 Windows 949 코드 페이지를 지원하지 않는다고 해서, KO16MSWIN949 캐릭터셋을 가진 데이터베이스 인스턴스를 생성할 수 없다는 것은 아니다. 데이터베이스 캐릭터셋과 운영체제의 캐릭터셋은 전혀 별개라고 인식해야 한다. 비록 Windows-949는 특정 업체의 문자셋이기는 하지만, 이를 기반으로 한 KO16MSWIN949 캐릭터셋은 한글 2350자의 한계를 가진 KO16KSC5601의 대안으로 용이하게 이용될 수 있다. 기억하자. 
    Unix에서든 Linux에서든, KO16MSWIN949 캐릭터셋을 가진 데이터베이스 인스턴스를 생성할 수 있다.

    3. UTF8/AL32UTF8

    UTF8은 유니코드를 구현한 캐릭터셋 중에 가변길이 인코딩 방식을 택하고 있는 캐릭터셋이다. 
    자세한 인코딩 방식은 여기에서 논할 필요가 없지만, 가변 길이를 위해 일종의 플래그 비트를 각 바이트마다 
    포함시켜야 하다보니, 한 글자를 표한하는데 필요한 바이트의 길이가 최대 3바이트(AL32UTF8의 경우 6바이트)까지 늘어날 수 있다.

    유니코드는 잘 알려진 바와 같이 현대 한글 11172자를 모두 가나다 순으로 잘 정렬된 상태로 포함하고 있다. 
    그래도 한글 한 자가 3바이트의 물리적 공간을 차지하므로, 오로지 모든 한글을 지원한다는 이유만으로 사용하는 것은 곤란하다. 하지만, 한글 이외에도 다른 언어들을 함께 데이터베이스에 저장해야 한다면 다른 선택의 여지가 없는 유일한 선택이 된다.

    한글을 지원하는 캐릭터셋 비교 테이블

    KO16KSC5601 KO16MSWIN949 UTF8 AL32UTF8
    한글 지원상태 한글 2350자 KO16KSC5601 + 확장 8822자(총 11172자)
    한글 11172자
    한글 11172자
    캐릭터셋/인코딩 버전
    한글완성형
    완성형코드포함
    확장된 8822자는 MS Windows Codepage 949에 따라 배열
    8.1.6 이전 : Unicode 2.1
    8.1.7 이후: Unicode 3.0
    9i Rel1: Unicode 3.0
    9i Rel2 : Unicode 3.1
    10g Rel1 : Unicode 3.2
    1/0g Rel2 : Unicode 4.0
    한글바이트
    2바이트
    2바이트
    3바이트
    3바이트
    지원버전
    7.x
    8.0.6 이상
    8.0 이후
    9i Release 1 이상
    Database Characterset으로 설정 가능 여부
    가능
    가능
    가능
    가능
    National Characterset으로 설정 가능 여부
    불가능
    불가능
    가능
    불가능
    한글정렬
    단순 바이너리 정렬로  구현 가능
    KOREAN_M 또는 UNICODE_BINARY 등 특수한 옵션 필요
    (한글 정렬에 관한 설명 참조)
    한글 정렬은 단순 바이너리 정렬로 가능. 한자 정렬은 KOREAN_M 옵션 필요

    장점
    - 특별한 장점이 없음. 완성형 코드만을 입출력하는 것이 확실할 경우에는 높은 성능
    - 2바이트로 모든 한글 저장/입출력 가능. 공간의 소모가 적으면서도 모든 한글을 입출력할 수 있다


    - 현대 한글 11172자가 정확한 순서로 배열되어 정렬이 효과적
    - 다른 언어들(중국어 태국어 등) 또한 같은 데이터베이스 인스턴스에 저장되어야 할 경우 UTF8 등의 유니코드 캐릭터셋 이외에 다른 대안이 있을 수 없음
    - 한글 지원은 UTF8과 동일
    단점
    - 한글을 2350자밖에 지원하지 못한다는 치명적인 단점이 있어 미래에는 사용이 자제되어야 할 캐릭터셋
    - 완성형과 호환을 하려다보니, 글자배열순서와 정렬 순서가 다르게 됨. 단순한 "ORDER BY" 절로는 제대로 한글 정렬을 할 수 없음
    한글 한 캐릭터가 3바이트를 소모하게 되어 공간의 소모가 크고, 유니코드 인코딩/디코딩에 성능을 소모해야 한다


    4. National Characterset

    네셔널 캐릭터셋은 유니코드를 지원하지 않는 캐릭터셋을 가진 데이터베이스에서 유니코드를 지원하기 위해 부가적으로 설정할 수 있는 캐릭터셋이다. 즉, 하나의 데이터베이스 인스턴스는 "캐릭터셋"과 "네셔널 캐릭터셋"을 가진다. 처음 시스템 구축 당시와는 달리, 한글 이외의 다른 언어를 급히 저장해야 할 필요성이 있는 경우 네셔널 캐릭터셋을 적절히 활용할 수 있다.

    네셔널 캐릭터셋을 가능한 캐릭터셋은 단 두 가지이다. UTF8과 AL16UTF16(기본값).
    네셔널 캐릭터셋을 사용하기 위해서는 특정 타입으로 테이블의 컬럼 또는 PL/SQL 변수를 선언해야 한다. CHAR와 VARCHAR2,CLOB에 대응되는 네셔널 캐릭터셋 기반의 타입으로는 NCHAR, NVARCHAR2,NCLOB이 있다.

    즉, KO16MSWIN949 데이터베이스에서 다음과 같이 테이블을 생성할 경우,

      CREATE TABLE test_table
      ( varchar_value VARCHAR2(2000),
      nvarchar_value NVARCHAR2(2000)
      );
    "varchar_value" 컬럼에는 KO16MSWIN949에 속하는 글자들만 저장할 수 있는 반면, nvarchar_value 컬럼에는 유니코드에 속한 모든 글자들을 저장할 수 있다. 약간의 부가적인 코드가 필요할 뿐, 실제 프로그래밍 방식은 거의 같다.

    5. 캐릭터셋 선택의 원칙

    많은 원칙이 필요없다. 다음 몇가지만 기억하자.

    • 한글 지원을 위해서는 반드시 위의 네 가지 캐릭터셋 중에 하나를 선택해야 한다 
    • 한국에서만 사용하는 시스템이라면 KO16MSWIN949를 선택한다 
    • 한국어 뿐 아니라 중국어, 일본어, 러시아어 등 다양한 언어로 된 데이터를 저장해야 한다면 UTF8, AL32UTF8을 선택한다. 인코딩 변환으로 한국어 기반의 캐릭터셋에 비해 속도의 저하가 있다고 알려져 있다. 
    • 대부분이 한글이며, 일부 외국어가 필요하다면, 한국어 기반의 캐릭터셋(KO16MSWIN949)을 사용하되, National Characterset을 이용한 컬럼에 외국어를 저장한다.

    PartⅣ. 올바른 NLS 환경변수값 설정하기.

      "모로 가도 서울만 가면 된다"고 자동차에 대한 지식없이 어떻게 하다보니까 차를 뒤로 움직이게 되어, 후진으로만 목적지에 도달한다면 과연 목적지에 잘 도달했다고 칭찬받아야 할까? 그 답은 "예"일 수도, "아니오"일 수도 있지만, "모로 가도 한글만 나오면 된다"는 식으로 구축된 시스템에 대해서는 무조건 "아니오"가 답이다. 많은 시스템들이 제대로 운전에 대한 지식과 준비 없이 시스템을 목적지로 운전시키고 있다.

      오라클 데이터베이스는 무려 20개의 다양한 NLS 환경변수를 제공한다. 하지만 염려는 붙들어 매길...... 그렇다고 해서 20개 모두의 씀씀이를 다 알아야 한국어데이터를 제대로 다룰 수 있다는 의미는 아니다. "손가락만 까딱해도 된다"는 요즘 자동차들이 제공하는 수많은 기능들 중에 핵심적인 기능만 알아도 안전운전을 할 수 있듯이, 몇 가지 핵심적인 변수의 의미만 제대로 파악하고 제대로 사용한다면 그야말로 "안전한" 한국어 환경을 구축할 수 있는 것이다.

      1. NLS_LANG

      1) NLS_LANG 변수의 구성
      NLS_LANG 변수는 단순히 하나의 변수가 아니라 실질적으로 NLS 연산의 모든 것을 결정한다고 해도 틀리지 않은 세 가지 정보를 포함하고 있는 중요한 변수이다.

          NLS_LANG = [언어]_[영역].[캐릭터셋]


      정 의
      가능한 값
      언어
      현재 사용자가 사용하는 언어적 특성을 결정짓는 값 SQL> select parameter,value from V$NLS_VALID_VALUES where parameter like '%LANG%' ORDER BY value;
      ...

      KOREAN
      LATIN AMERICAN SPANISH
      ...
      TRADITIONAL CHINESE
      ..
      VIETNAMESE
      영역
      현재 사용자가 위치한 영역의 특성을 결정짓는 값 SQL> select parameter,value from V$NLS_VALID_VALUES where parameter like '%TERR%' ORDER BY value;
      ...

      KOREA
      ...
      SAUDI ARABIA
      ...
      YUGOSLAVIA
      캐릭 터셋
      현재 사용자의 시스템이 인식할 수 있는 캐릭터셋의 값 SQL> select parameter,value from V$NLS_VALID_VALUES where parameter like '%CHARACTERSET%' ORDER BY value;

      ..
      KO16KSC5601
      ..
      KO16MSWIN949
      ..
      WE8DEC

      ..
      ZHT16MSWIN950

      KOREAN_KOREA.KO16KSC5601
      KOREAN_KOREA.KO16MSWIN949
      AMERICAN_AMERICA.US7ASCII
      AMERICAN_AMERICA.WE8ISO8859P1
      JAPANESE_JAPAN.JA16SJIS
      .KO16MSWIN949
      .UTF8

       

      2) NLS_LANG 변수값 설정의 기본 원칙
      "제 데이터베이스가 UTF8인데, NLS_LANG도 .UTF8로 해야 하는 거 아닌가요?"
      땡, 틀렸다. NLS_LANG 변수가 데이터베이스 캐릭터셋과 값이 항상 같아야 한다면 무엇하러 설정하겠는가?

      "NLS_LANG 변수는 데이터베이스에게 사용자의 환경을 알려주는 인식표 역할을 한다"
      그렇다. NLS_LANG 변수의 값은 멀리 있는 데이터베이스의 환경이 아니라, 사용자 자신이 속해 있는 환경을 도리어 데이터베이스에 알려주는 역할을 하는 변수이다.

      NLS_LANG을 다음과 같이 설정해 보자.

      > set NLS_LANG=AMERICAN_AMERICA.KO16MSWIN949

      이는 사용자 자신이 미국의 영어를 쓰고, 아메리카 영역 내에 있으며, 가진 컴퓨터가 사용하는 캐릭터셋은 KO16MSWIN949라는 의미이다.

      > sqlplus scott/tiger
      SQL> select x from y;
      select x from y
      *
      ERROR at line 1:
      ORA-00942: table or view does not exist


      SQL> select hiredate from emp;

      HIREDATE
      ------------
      17-DEC-80
      ...

      18 rows selected.

      이와 같이 모든 메시지나 날짜 형식을 미국에 맞게 표현해 준다.

      SQL> exit

      이제 NLS_LANG을 다음과 같이 변경해 보자.

      > set NLS_LANG=KOREAN_KOREA.KO16MSWIN949

      이는 사용자 자신이 한국어를 쓰고, 한국 영역 내에 있으며, 가진 컴퓨터가 사용하는 캐릭터셋은 KO16MSWIN949라는 의미이다.

      SQL> select hiredate from emp;

      HIREDATE
      --------

      80/12/17
      ..

      18 개의 행이 선택되었습니다.


      이제 한국의 날짜 형식과 함께 한국어로 메시지를 보여주게 된다.


      이에 따라, Windows 운영체제에서 한국어 환경을 사용하는 사용자들은 다음과 같이 NLS_LANG 값을 설정할 수 있다.
      KOREAN_KOREA.KO16MSWIN949 

      그리고, 유닉스 운영체제에서 한국어를 입출력한다면 다음과 같이 NLS_LANG을 설정할 수 있다. 
      KOREAN_KOREA.KO16KO16KSC5601 

      데이터베이스가 UTF8이든 KO16MSWIN949이든 상관없이 한글을 지원하는 데이터베이스와 통신한다면 반드시 위와 같이 NLS_LANG값을 설정해야 한다.


      3) 데이터베이스의 캐릭터셋과 동일한 값으로 캐릭터셋을 설정하는 경우
      데이터베이스의 데이터는 사용자로 전달될 때, NLS_LANG에 설정된 캐릭터셋에 기반해 적절히 변환되어 전달된다. 
      즉 내부적으로는 한글이 UTF8로 저장되어 있다 할 지라도, NLS_LANG의 값에 따라 UTF8로 인코딩된 문자열을 Windows 949 코드 페이지로 변환하여 사용자에게 전달하게 되는 것이다.

      NLS_LANG 값에 있는 캐릭터셋을 UTF8로 설정하는 경우가 꼭 없는 것은 아니다. 만일 데이터베이스의 캐릭터셋이 UTF8인 상태에서, 질의를 요청한 사용자의 NLS_LANG의 값도 .UTF8이라면, 데이터베이스에서는 단순히 UTF8 문자열을 사용자에게 전달하게 되며 이를 어떻게 사용할 지는 사용자의 몫에 달려 있다.

      NLS_LANG 값을 데이터베이스 캐릭터셋에 맞추는 경우는 대략 다음과 같은 경우가 있다. 

      • 데이터베이스로부터 데이터를 export받을 때
      • export 받은 데이터베이스와 같은 캐릭터셋을 가진 데이터베이스로 export된 파일을 import할 때
      • 기타 다국어 지원 애플리케이션에서 목적에 따라 사용할 수 있다.

       

      [주요 NLS 변수 요약]

      변수명
      정의
      기본값 설정
      설정방법
      NLS_TERRITORY
      영역 설정
      NLS_LANG 변수값에 의해 자동 설정
      초기화변수
      ALTER SESSION SET NLS_TERRITORY = 'KOREA'
      NLS_LANGUAGE
      언어 설정
      NLS_LANG 변수값에 의해 자동 설정 초기화변수
      ALTER SESSION SET NLS_TERRITORY = 'KOREAN'
      NLS_LANG
      언어,영역, 캐릭터셋
      설정
      AMERICAN_AMERICA.US7ASCII
      OS 환경변수
      NLS_COMP
      SQL에서의 비교 방식(<,>,=)
      설정
      BINARY값으로 비교
      초기화변수,OS환경변수
      ALTER SESSION SET NLS_COMP = ''
      NLS_SORT
      문자열의 정렬방법 설정
      NLS_LANGUAGE값에 따라 결정
      초기화변수,OS환경변수
      ALTER SESSION SET NLS_SORT = 'KOREAN_M'
      NLS_TERRITORY 변수에 따라 그 값이 결정되는 변수:
      • NLS_CREDIT(대차대조표 '대변'항목의 금액표기를 위한 기호. 보통 '공백'문자)
      • NLS_CURRENCY
      • NLS_DATE_FORMAT
      • NLS_DEBIT(대차대조표 '차변'항목의 금액표기를 위한 기호. 보통 '마이너스'문자)
      • NLS_ISO_CURRENCY
      • NLS_LIST_SEPARATOR(숫자를 가로로 나열할 때 각 숫자를 구분하는 기호로, 우리나라의 경우 콤마이다)
      • NLS_MOMETARY_CHARACTERS(금액 표기시 금액을 읽기 쉽게 나누는 문자로 우리나라에서는 3자리마다 ","를 추가한다)
      • NLS_NUMERIC_CHARACTERS(소수점기호와 숫자 그룹핑을 위한 문자 설정. 우리나라에서는 '.,'이다(dot와 comma)
      • NLS_TIMESTAMP_FORMAT
      • NLS_TIMESTAMP_TZ_FORMAT
      • NLS_DUAL_CURRENCY(유로화 변경 기간동안의 혼란을 막기 위해 만들어진 매개변수. 9i Release 2과 그 이후로는 EU의 유로화 변경이 완료된 상태로 NLS_CURRENCY와 값이 동일하다. 다만 미래에 다른 지역에서도 통화기호의 변경이 일어나면 사용될 수 있다)

      NLS_LANGUAGE 변수에 따라 그 값이 결정되는 변수:
      • NLS_DATE_LANGUAGE
      • NLS_SORT

      *초기화변수 : init.ora또는 spinit.ora에 설정되어 데이터베이스 구동시에 자동 설정될 수 있는 변수를 의미함
      *OS환경변수 : OS에서 초기화 방식(setenv, export 등)으로 값을 할당,변경할 수 있는 변수

    PartⅤ. KO16KSC5601에 서 지원되지 않는 글자들을 KO16KSC5601 데이터베이스에 입출력하기.

    이제는 KO16KSC5601 캐릭터셋은 “ㅤㅅㅛㅍ”, “ㅤㅃㅙㄼ”과 같은 글자를 지원하지 않는다는 것을 알게 되었을 것이다. 
    그저 한글 하면 “KSC5601”을 떠 올리는 것이 보통이니 이런 사실을 아는 것만으로 획기적인 사고의 전환이 가능하다
    .

    “우리 시스템은 일반 사용자의 다양한 의견을 게시판을 통해 수렴하고 있다. 어떤 한글이 입력될 지 모르는 상황이다. KO16KSC5601”은 한글을 2350자밖에 지원하지 않으므로 사용하지 않아야겠다."

    이런 매끄러운 사고가 가능한 것이다. 그러면 어떻게 KO16KSC5601에서 지원되지 않는 글자들을 KO16KSC5601” 데이터베이스에 입출력할 수 있는가? 결론은 이렇다.

    “그런 일은 있어서도 안 되고, 가능하지도 않다” 
    실망스러운가? 하지만 엄연한 사실이며, 여러분들은 이 말을 아주 심각하게 받아들여야 한다. 하지만 여전히 여러분들보다 
    생각이 덜 하는 사람들, 심각하게 받아들이지 않는 사람들이 있다.

    “KO16KSC5601”에 얼마든지 그런 글자들을 삽입할 수 있던데요? 
    그래서 그런 사람들에게 직접 예를 보여줄 수 있다. 한글 윈도우 상에서 다음과 같은 질의를 보여주자. 
    한글 Windows 운영체제이므로 암시적으로 NLS_LANG의 값은 KOREAN_KOREA.KO16MSWIN949라고 생각하면 된다.

    DOS> sqlplus scott/tiger@KSC5601DB

    SQL> create table charset_test(charcol VARCHAR2(100), ncharcol NVARCHAR2(100));

    SQL> insert into charset_test(charcol) values('ㅤㅅㅛㅍ');

     

    1 개의 행이 만들어졌습니다.

     

    SQL> select charcol from charset_test;

     

    CHARCOL

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


    “ㅤㅅㅛㅍ”이라는 글자는 INSERT 문장 실행시 에러가 발생되지는 않았지만 결국 제대로 된 값이 삽입되지 않았다. 
    여기서 다시 “삽입할 수 있다”파는 다음과 같은 주장을 펼칠 수 있다.

    “NLS_LANG =.KO16KSC5601로 설정하면 결국 캐릭터셋과 NLS_LANG 값이 일치하여 제대로 삽입할 수 있을 것이다.” 

    그래서 이렇게 시도를 할 수 있다. 어떤 결과가 나오는지 살펴보자. 

    DOS> set NLS_LANG=.KO16KSC5601

    DOS> sqlplus scott/tiger@KSC5601DB

    SQL> insert into charset_test(charcol) values('ㅤㅅㅛㅍ');

    ERROR:

    ORA-01756: quoted string not properly terminated


    이번에는 오히려 INSERT 문장 자체가 실패한다. 어떻게 이런 결과가 나올까? 앞서 NLS_LANG에 대해 배운 바로는 분명히 NLS_LANG 변수와 데이터베이스의 캐릭터셋이 일치할 경우 데이터의 변환 없이 데이터가 그대로 삽입되게 된다. 
    그런데 삽입 문장 자체가 실패하다니? 하지만 놀랄 필요없다. 그저 이것은 당연한 결과이다. 개발자 여러분은 최대한 단순해질 필요가 있다. 개발자들은 가끔 너무 잘 하려고 하는 경향이 있으며, 안 되는 것은 없다고 생각한다. 
    하지만 모든 꼼수는 결국 가까운 미래에 자기 자신에게 부메랑이 되어 격무의 부담으로 돌아온다는 사실을 꼭 기억하자.

    “지원되지 않는 글자를 데이터베이스에 삽입하지 말자” 

    인정하건대 글자에 따라, 프로그래밍 방식에 따라 지원되지 않는 글자들로 연산이 가능한 때가 있다. 
    단순히 NLS_LANG과 데이터베이스 캐릭터셋이 일치하는 바람에 들어가지 못할 데이터가 들어가게 되는 경우가 있다. 
    예를 들어 위의 “ㅤ숖”자 대신에 역시 완성형에서 지원되지 않는 글자인 “똠”을 테스트하면 엉뚱하게 들어가게 된다. 
    그렇지만 이것은 단순히 “우연의 일치”일 뿐이다. 1바이트와 2바이트가 섞여 있는 캐릭터셋에서 한 글자의 끝을 인식하는 과정에서 우연히도 “ㅤ똠”이 그 과정을 무사히 통과했을 뿐이다. 그렇지 않으면 대부분 “ㅤ숖”과 같이 엉뚱하게 “문 자열이 제대로 끝나지 않았다”는 에러를 만나게 된다. 확인할 겸 몇 글자 더 시도해 보기로 하자.

    DOS> set NLS_LANG=.KO16KSC5601

    DOS> sqlplus scott/tiger@KSC5601DB

    SQL> insert into charset_test(charcol) values('숖');

    ERROR:

    ORA-01756: quoted string not properly terminated

     

     

    SQL> insert into charset_test(charcol) values('똠');

     

    1 row created.

     

    SQL> insert into charset_test(charcol) values('믜');

    ERROR:

    ORA-01756: quoted string not properly terminated

     

     

    SQL> insert into charset_test(charcol) values('뾃');

     

    1 row created.

     

    SQL> insert into charset_test(charcol) values('햏');

     

    1 row created.

     

    SQL> select charcol from charset_test;

     

    CHARCOL

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

     

    햏/p>


    일부 OCI나 MS 기반의 애플리케이션에서 NLS_LANG과 캐릭터셋이 일치하는 바람에 지원되지 않는 글자들을 삽입하고 조회할 수 있기도 한다. 지금 얼마나 많은 오라클 데이터베이스 인스턴스에서 이런 현상이 벌어지고 있는지 상상하기조차 힘들다. 하지만 미래에는 그런 일이 일어나지 않아야겠다. 왜냐하면 그런 잘못된 구성으로 인해 발생하는 힘든 일은 모두 개발자가 감당하고 있기 때문이다.

    PartⅥ. 오라클 데이터베이스에서의 한글 정렬.

    1. KO16KSC5601 데이터베이스

    KO16KSC5601에서는 한글 2350자의 바이너리 정렬 순서가 한글의 언어적 정렬 방식과 동일하다. 따라서, 단순한 ORDER BY 명령어만으로 정렬의 효과를 거둘 수 있다. 그리고, 한자의 경우 한글 뒤에 한자의 음에 맞게 정렬이 된다. 예에 나와 있는 한자들은 "가구","류","애"로 모두 잘 정렬되어 있는 것을 볼 수 있다. 하지만 명심하라, 단지 한글 2350자들과 한자 4888자의 정렬일 뿐이다. 나머지 글자들에 대해서는 입출력도 불가능하거니와, 입출력을 무슨 수를 써서 했다고 하더라도 정렬이 제대로 될 리가 없다.

    SQL> SELECT text FROM nlstech_sample_sort_ksc5601 ORDER BY text;

     

    TEXT

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

    가나다라마바사

    라디오를켜라

    可口

     

    // 한글, 한자의 정렬이 올바르다

    실제로 잘 만들어진 프레임워크의 내부를 보면 좋다고 알려진 패턴을 구현한 것인 경우가 많고 구조는 잘 정의된 패턴에 기반하고 있는 경우가 일반적이다. 프레임워크는 디자인 패턴들을 실제로 구현한 결과이고 프레임워크를 문서화할 때는 패턴을 통해서 기술하는 것이 효과적이다.

    2. UTF8/AL32UTF8 데이터베이스

    UTF8 데이터베이스의 경우, 한글만을 고려하면 별다른 정렬 옵션이 필요없다. 왜냐하면 한글 11172자의 정렬 순서와 바이트 코드 정렬 순서가 일치하기 때문이다. 한글만의 정렬에 관해서는 정확성과 성능을 모두 만족한다고 볼 수 있다. 

    데이터베이스에서 예제 실행:

    SQL> SELECT * FROM nlstech_sample_sort_ko ORDER BY text;

    TEXT

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

    가나다라마바사

    똠각하

    라디오를켜라

    먄해

    햏햏

     

    // 정렬 결과가 올바르다


    하지만, 한자까지 고려한다면 이것도 역시 완벽한 방법은 아니다. 다음 예제를 보자.

    SQL> SELECT text FROM nlstech_sample_sort_unicode_b ORDER BY text;

     

    TEXT

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

    可口

    가나다라마바사

    똠방각하

    라디오를켜라

    먄해

    햏햏


    한자의 경우도 단순히 유니코드 내의 바이트 코드값에 의존하게 되면 별다른 의미를 찾을 수 없는 배열만을 얻게 된다. 이 경우에는 특정 NLS_SORT 매개변수 값을 지정함으로써 좀 더 의미있는 결과를 얻을 수 있다.

    NLS_SORT=’KOREAN_M’ 이용

    NLS_SORT 값을 설정할 수 있는 방법에 대해서는 이미 앞에서 설명한 바가 있다.

    9i부터 지원되는 ‘KOREAN_M’ 정렬 방식을 이용하면 한자에 관련되어 몇 가지 원칙이 적용된다.
    • 한글은 단순히 유니코드 바이트 정렬에 의존한다
    • 모든 한글은 한자에 우선한다
    • 한자는 발음 순서대로 정렬된다

    한 마디로 KO16KSC5601에서 사용되던 정렬 방식으로 모든 한글과 한자를 정렬하겠다는 방법이다.

    KOREAN_M 사용:

    SQL> SELECT text FROM nlstech_sample_sort_unicode_b ORDER BY NLSSORT(text,'NLS_SORT=KOREAN_M');
     

    TEXT

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

    가나다라마바사

    똠방각하

    라디오를켜라

    먄해

    햏햏

    可口

    // 한자가 발음 순서대로 정렬되어 있다(가    애)

    3. KO16MSWIN949 데이터베이스

    KO16MSWIN949는 KO16KSC5601에서 지원되지 않는 8822자의 한글을 추가적으로 지원한다는 점에서 KO16KSC5601의 대안으로 자주 이용되는 캐릭터셋이다. 하지만, 기존 KO16KSC5601의 수퍼셋으로 군림하려다 보니 총 11172자의 한글의 바이트 코드가 한글의 언어적 정렬 순서와 불일치할 수 밖에 없다.

    KO16MSWIN949 데이터베이스에서 실행:

     

    SQL> SELECT * FROM nlstech_sample_sort_unicode_b ORDER BY text;

     

    TEXT

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

    똠방각하

    먄해

    가나다라마바사

    라디오를켜라

    햏햏

     

    // 정렬 결과가 엉뚱하다


    이를 극복하기 위해서는 단순한 바이트코드 정렬방식이 아니라 다른 정렬방식을 적용해야 한다.

    방법 1) NLS_SORT=’UNICODE_BINARY’ 이용 

    현재 사용자 세션의 기본 정렬 방식을 변경하여 해결:

     

    SQL> ALTER SESSION set NLS_SORT = 'UNICODE_BINARY';

     

    세션이 변경되었습니다.

     

    SQL> SELECT * FROM nlstech_sample_sort_unicode_b ORDER BY text;

     

    TEXT

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

    가나다라마바사

    똠방각하

    라디오를켜라

    먄해

    햏햏



    질의 자체에 NLS_SORT 값을 지정해 주는 방법:

     

    SQL> SELECT text FROM nlstech_sample_sort_ko ORDER BY NLSSORT(text,'NLS_SORT=UNICODE_BINARY');

     

    TEXT

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

    가나다라마바사

    똠각하

    라디오를켜라

    먄해

    햏햏



    방법 2) NLS_SORT=’KOREAN_M’ 이용 

    한자 데이터까지 고려하면 ‘KOREAN_M’을 사용하는 것이 좋다.

    UNICODE_BINARY 사용:

     

    SQL> SELECT text FROM nlstech_sample_sort_korean_m ORDER BY NLSSORT(text,'NLS_SORT=UNICODE_BINARY');

     

    TEXT

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

    可口

    가나다라마바사

    똠방각하

    라디오를켜라

    먄해

    햏햏

     

    // 한자의 정렬 또한 단순히 유니코드 바이트 코드에 의존한다.



    KOREAN_M 사용:

    SQL> SELECT text FROM nlstech_sample_sort_korean_m ORDER BY NLSSORT(text,'NLS_SORT=KOREAN_M');

     

    TEXT

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

    가나다라마바사

    똠방각하

    라디오를켜라

    먄해

    햏햏

    可口

     

    // 한자가 발음 순서대로 정렬되어 있다(가    애)

    4. 인덱스를 이용하여 성능 향상시키기.

      미리 NLSSORT를 이용한 인덱스를 생성하면 비록 디스크 공간이 더 필요하기는 하지만, 편리하고 성능도 좋아진다. 
      인덱스는 각 NLS_SORT 값별로 여러 개를 생성할 수 있다. 어느 인덱스를 사용할 것인지는 NLS_SORT 변수의 값에 달려 있다.

      KO16MSWIN949 데이터베이스에서 실행:

       

      SQL> CREATE INDEX nlstech_sample_sort_ub ON nlstech_sample_sort_ms949 (NLSSORT(text, 'NLS_SORT = UNICODE_BINARY'));

       

      인덱스가 생성되었습니다.

       

      SQL> CREATE INDEX nlstech_sample_sort_km ON nlstech_sample_sort_ms949 (NLSSORT(text, 'NLS_SORT = KOREAN_M'));

       

      인덱스가 생성되었습니다.

       

      SQL> ALTER SESSION set NLS_SORT = 'UNICODE_BINARY';

       

      세션이 변경되었습니다.

       

      // UNICODE_BINARY 인덱스가 사용된다

      SQL> SELECT text FROM nlstech_sample_sort_ms949 ORDER BY text;

       

      TEXT

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

      可口

      가나다라마바사

      똠방각하

      라디오를켜라

      먄해

      햏햏

       

      8 개의 행이 선택되었습니다.

       

      SQL>

      SQL> ALTER SESSION set NLS_SORT = 'KOREAN_M';

       

      세션이 변경되었습니다.

       

      // KOREAN_M 인덱스가 사용된다

      SQL> SELECT text FROM nlstech_sample_sort_ms949 ORDER BY text;

       

      TEXT

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

      가나다라마바사

      똠방각하

      라디오를켜라

      먄해

      햏햏

      可口

       

      8 개의 행이 선택되었습니다.



      NLS_SORT의 값으로 설정할 수 있는 것은 버전 별로 다를 수 있다. 이는 각 버전마다 존재하는 Globalization Support Guide(8.1.7 이전에는 Nationa Language Support Guide)를 참조하면 부록에서 발견할 수 있다. 하지만 본인이 운영하는 데이터베이스라면 대략 다음과 같은 질의로 확인할 수 있다.

      SQL> select value from V$NLS_VALID_VALUES where parameter = 'SORT' ORDER BY value
      ..
      BINARY
      ...
      KOREAN_M
      ...
      UNICODE_BINARY
      ..

    PartⅦ. 오라클 데이터베이스에서 한글 비교하기.

    앞선 팁에서 짐작할 수 있듯이, 정렬이 원하는 대로 되지 않는다면, 비교 또한 제대로 될 리가 없다. 두꺼운 책의 마지막에 있는 인덱스(찾아보기)나 사전처럼, 한글 키워드를 ‘가나다’ 그룹으로 만들기 위해서는 정렬과 비교가 필수적인 기능이다.

    "오라클의 기본 비교 방식은 BINARY"

    정렬도 그렇지만, 비교할 경우에도 역시 오라클 데이터베이스는 기본적으로 BINARY 방식을 사용한다. 같은 글자라 해도 이진 코드는 각 캐릭터셋마다 다르므로 어떤 캐릭터셋에서는 “>”의 결과가 나왔던 비교문도 다른 캐릭터셋에서는 “<”의 결과가 나올 수도 있다. 우리는 이미 어떤 정렬 방식을 지정하면 제대로 정렬이 되는지 앞선 팁에서 익혔으므로, 이제 여기서는 오라클이 비교를 수행할 경우에도 내부적으로 같은 방식을 사용할 것을 지시하기만 하면 된다.

    1.NLS_COMP.

    NLS_COMP 변수는 오라클이 비교 연산들을 수행할 시 어떤 방식을 사용할 지를 지정하는 변수이다. 복잡한 NLS_SORT와는 달리 이 변수의 값으로는 오로지 BINARY 또는 ANSI 두 값만이 허용된다. BINARY는 기본으로 설정되어 있는 값으로 비교 연산은 이진 바이트 코드의 비교로 이루어진다는 것을 의미한다. 하지만, ANSI라고 설정하면, 같은 비교 연산자의 수행 방식이 이진 비교에서 언어적 비교(Linguistic Comparison)으로 전환된다. 즉, 우리가 NLS_SORT 변수에 설정한 정렬 방식이 비교 연산자(>, <, BETWEEN 등)들에게 약발이 먹힌다는 의미이다.

    NLS_SORT와는 달리 이 값은 NLSSORT 함수의 내부 패러미터가 아니므로, SQL 질의문 내에 사용될 수는 없다. 
    데이터베이스 초기화 패러미터(init.ora)로 지정되거나 환경 변수, 세션 변수로 지정되어야 한다.

    ALTER SESSION SET NLS_COMP=ANSI;
    또는 
    ALTER SESSION SET NLS_COMP=BINARY;


    그렇다면 다음을 살펴보자.

    KO16MSWIN949 데이터베이스에서:

    SQL> SELECT text FROM nlstech_sample_comp_ms949;

    TEXT

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

    루비

    다대기

    두상이크다

    병원

    도망

    나름대로

    도도하다

    똠방각하

    거위

    도시남녀

    너도밤나무

     

    TEXT

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

    구씨가족

    라면

     

    13 개의 행이 선택되었습니다.


    정렬되지 않은 이 문자열들의 집합에서 ‘ㄷ’ 에 속한 문자열들만을 추출하기를 원한다고 치자. 
    그렇다면 먼저 다음과 같이 시도해 볼 수 있다.

    SQL> SELECT text FROM nlstech_sample_comp_ms949 WHERE text >= '다' AND text < '라';

     

    TEXT

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

    다대기

    두상이크다

    도망

    도도하다

    도시남녀


    제대로 된 것 같지만, 똠방각하가 누락되었다. 그 이유는 짐작할 수 있을 것이다.
    KO16KSC5601에 포함되어 있지 않은 이라는 글자는 아쉽게도 이진 정렬에서는 제대로 된 결과를 보여주지 못한다. 
    그래서 이번에는 다음과 같이 시도해 보기로 한다.

    SQL> ALTER SESSION SET NLS_SORT='KOREAN_M';

    세션이 변경되었습니다.

    SQL> SELECT text FROM nlstech_sample_comp_ms949 WHERE text >= '다' AND text < '라';

     

    TEXT

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

    다대기

    두상이크다

    도망

    도도하다

    도시남녀


    애석하게도 결과는 똑같다. 분명히 NLS_SORT 값을 ‘KOREAN_M’으로 설정하여 정렬 시 유니코드 이진 값을 기준으로 정렬할 것을 명시했지만, 비교 연산자의 수행 시, 이 원칙이 지켜지지 않았다. 그래서 이번에는 비교 연산을 수행할 때, NLS_SORT에서 명시한 언어별 정렬 방식을 사용할 것을 명령해 보자. 

    SQL> ALTER SESSION SET NLS_SORT='KOREAN_M';

     

    세션이 변경되었습니다.

     

    SQL> ALTER SESSION SET NLS_COMP=ANSI;

     

    세션이 변경되었습니다.

     

    SQL> SELECT text FROM nlstech_sample_comp_ms949 WHERE text >= '다' AND text < '라';

     

    TEXT

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

    다대기

    두상이크다

    도망

    도도하다

    똠방각하

    도시남녀

     

    6 개의 행이 선택되었습니다.


    이제야 비로소 제대로 된 결과가 나오는 것을 알 수 있다.


     

    PartⅧ. 글을 마치며.

    이상으로 오라클이 어떤 방식으로 NLS를 지원하는가를 한국어를 중심으로 알아보았다. 쉬운 내용이지만, 그만큼 많은 사람들이 간과하기도 쉬운 것으로, 아무리 "나는 한국에 존재하는 시스템만을 위한 소프트웨어 개발자로소이다"라고 해도 NLS에 대한 개념을 가진 상태에서 개발을 진행하는 것은 매우 중요하다. 오라클이라는 소프트웨어는 기본적인 설계부터 NLS를 고려해 만들어졌다. 시스템을 구축할 때 첫 출발을 잘 한다면, 그 어떤 소프트웨어보다도 강력하게 지원되는 NLS 기능을 마음껏 누릴 수 있을 것이다. 다음 회에는 조금 더 실용적인 사례를 살펴보기로 하겠다

  •  

    Posted by redkite
    , |

    원활한 성능을 위한 자동화, 모니터링 유지 관리 작업들

     

    정기적인 유지 관리는 원활하고 성공적인 데이터베이스 운영을 위해 필수적이다. 유지 관리는 데이터손실을 막기 위한 백업을 만드는 것을 포함하며, 데이터의 일관성 유지, 데이터와 인덱스간의 불일치를 막아주어 데이터 무결성을 확인하고 데이터의 단편화를 막아 정기적으로 인덱스를 갱신하는 것을 포함한다.

     

    SQL서버의 데이터베이스 유지 관리 계획은 유지 관리 운영의 정의, 자동화와 모니터링 하는 것을 도와준다. 유지 관리 계획의 생성을 통해 진행되는 데이터베이스 유지 관리 계획 마법사는 보통은 각각의 유지 관리 영역에 적절한 기본값을 선택한다.

     

    그러나 마법사를 사용하여 목적에 알맞은 작업을 하기 위해선 유지 보수 계획이 실행될 때 SQL 서버 T-SQL 명령들이 실행되는 각각의 옵션 내용이 어떻게 동작을 하는지 알아야 하고, 이 명령들이 데이터베이스의 성능에 어떻게 영향을 끼칠 것인지, 그리고 이 내용들은 디스크 공간의 필요에 대한 유지 관리 계획에 어떤 영향을 미칠지도 알 필요가 있다.

     

     

    데이터 베이스 유지 관리 계획 아키텍처

     

    데이터베이스 유지 관리 계획을 세우기 위해 데이터베이스 유지 관리 계획 마법사를 사용할 수 있다. 데이터베이스 유지 관리 계획 마법사는 하나 이상의 데이터베이스를 선택하는 것으로 시작된다. 그리고 나서 유지 관리 계획에 포함시킬 작업 내용을 포함시킨다. msdb 데이터베이스에 있는 몇몇의 시스템 테이블은 유지관리 작업 계획과 작업 내용에 대한 상세한 정보를 가지고 있다.


    SQL서버는 유지관리 계획을 생성할 때 sysdbmaintplan 테이블에 엔트리를 생성한다. 여러분의 계획이 정의한 작업(무결성 체크, 최적화, 백업)을 실행할 때 마다 SQL서버는 sysdbmaintplan_history 테이블에 해당 내용을 기입한다. 마법사의 첫 단계는 유지관리 계획이 필요한 데이터베이스를 정의하는 것이다. 선택한 데이터베이스는 sysdbmaintplan_databases 테이블에 저장된다. 이 테이블은 데이터베이스 유지 관리 계획에 관련된 각 데이터 베이스 정보를 담고 있는 하나의 열을 가진다. 만약 여러 개의 데이터베이스라는 옵션을 선택한 경우 database_name 컬럼에 모든 데이터베이스, 모든 사용자 데이터베이스, 모든 시스템 데이터베이스 중 하나의 항목을 포함한다.

    다음의 몇 단계는 최적화, 무결성 검사, 데이터베이스 백업, 트랜잭션 로그 백업 등 4개의 유지 관리 작업을 정의한다. 데이터베이스 유지관리 계획 마법사는 계획에 포함시킨 각각의 작업을 위한 SQL 서버 에이전트 작업을 생성하고 sysjobs 테이블에 이 작업들을 저장한다. 마법사는 sysdbmaintplan_jobs 테이블에 유지관리계획과 그 작업 사이의 링크를 저장한다.

     

    각 작업은 유틸리티 스위치를 포함하는 하나의 파라미터로 xp_sqlmaint 확장 저장 프로시저를 호출하는 하나의 단계를 가지고 있다. 유틸리티 스위치는 사용자 인터페이스가 없는 실행파일을 위한 커맨드라인 파라미터로 동등하게 생각해 볼 수 있다. SQL 서버는 데이터베이스 유지관리 계획을 백그라운드 프로세스로 사용자의 개입 없이 실행한다. 따라서 유틸리티 스위치는 그 계획이 실행할 때 무엇을 할지를 정의한 파라미터로써 작용한다. 마법사는 각 계획을 위해 선택한 옵션을 위한 이 스위치들을 만들어 준다. [그림 1]에 나와 있는 작업 명령은 유틸리티 스위치를 따라 sqlmaint 유틸리티를 호출하는 xp_sqlmaint의 예제이다. Sqlmaint 은 SQL 서버 데이터베이스 유지관리 계획의 핵심적인 부분이다. 이는 유틸리티 스위치를 처리하고, T-SQL 명령을 작성하고 그것을 SQL 서버로 보낸다. Sqlmaint는 또한 오래된 데이터베이스 백업을 삭제하고, 텍스트와 html 보고서 파일을 생성할 수 있다. BOL은 sqlmaint 유틸리티 스위치에 대한 리스트를 포함 하고 있다. 이 유지 관리 계획 시스템 테이블을 테스트하다가 흥미로운 점을 발견했다. 당신이 작성한 실행계획이나 선택한 각각의 옵션도 시스템 테이블에 저장되지 않는다는 점이다. 시스템 테이블은 오직 계획과 스케줄작업에 대한 링크만을 포함한다. 유지 관리 계획을 수정하기 위해 열었을 때 엔터프라이즈 관리자는 각 관련된 작업을 읽고 계획 생성시 선택한 옵션을 얻기 위해 xp_sqlmaint 호출을 처리한다. 예를 들어 만약 [그림 2]에서 보여지는 바와 같이 유지 관리 계획이 Optimizations에 관한 내용이며 페이지당 여유공간을 10%로 변경하는 것을 선택한다면 xp_sqlmaint 호출은 정의된 동작의 하나로 RebldIdx 10을 포함한다. RebldIdx 10 은 실행 시에 그 데이터베이스의 모든 인덱스를 다시 재빌드하여 각 인덱스 페이지가 10%의 여유공간을 하는 T-SQL 명령으로 변환된다.


    SQL서버는 유지 관리 계획 시스템 테이블에 이러한 정의 내용을 기록하지 않는다는 것은 중요하다. 유지 관리 계획의 구현은 합리적으로 이루어진다. 만약 이러한 정의들이 시스템 테이블에 저장된다면 누군가가 수작업으로 스케줄된 유지 관리 계획에 대한 작업 내용을 수정하는 것에 대해 막을 길이 없으므로, 시스템 테이블의 정의와 실제 스케줄상의 매치 되지 않는 정의가 생길 것이다. 
    sqlmaint 유틸리티를 실행하고 계획 이름이나 ID를 지정할 때 단지 SQL서버가 시스템테이블에서 검색하는 것은 계획에 포함된 데이터베이스 리스트이다. 이는 수동으로 유지관리계획을 실행시킬 때 sqlmaint 는 계획을 생성하기 위해 마법사를 사용할 때 지정한 동작이 아니고 실행 시에 파라미터로 지정한 액션을 실행한다는 것을 의미한다. 예를 들어 마법사를 이용하여 모든 데이터베이스에 대하여 C:\BACKUP 경로에 풀 백업을 실행하고 1주 이상 지난 백업파일을 삭제하는 계획을 작성했다면 마법사는 아래와 같은 정의를 만들어 낸다.

     

    EXECUTE master.dbo.xp_sqlmaint
    N"-PlanID 54442E44-EF8D-488A-
    AF39-FFBB3CE62D1D -BkUpMedia 
    DISK -BkUpDB "C:\BACKUP"
    -DelBkUps 1WEEKS "

     

    위의 코드를 복사하여 쿼리분석기에 붙여 넣고 이 실행계획에 있는 각각의 데이터베이스에 대하여 DBCC CHECKDB 만을 수행하도록 백업 스위치를 ? CkDB 로 대체한다.

     

    EXECUTE master.dbo.xp_sqlmaint
    N"-PlanID 54442E44-EF8D-488A-
    AF39-FFBB3CE62D1D -CkDB "

     

    수정된 코드를 실행시킬 때 sqlmaint는 유지 관리 계획을 실행시키지만 그 계획은 풀 백업에 대해 정의 했다 하더라도 풀 백업을 실행하지 않는다. 실행 시 스위치를 제공하지 않으므로 오직 무결성 확인만을 실행할 것이다.


    유지 관리 계획 아키텍처와 sqlmaints 유틸리티 스위치에 익숙해지면 무슨 조건 아래에서 어떤 옵션이 실행되는지를 얻기 위해 수동으로 유지 관리 계획에 대한 작업을 시작할 수 있다. 또한 동적으로 sqlmaint 스위치를 만들어낼 수 있으며 그 옵션을 조금 변경 후 계획을 실행 할 수도 있다. 예를 들면 백업을 하나 대신 두 개의 하드디스크에 저장하기 위해, 백업의 경로를 매번 변경할 수 있다. 당신은 유지 관리 계획을 마법사에서 하듯이 예정된 작업으로서 구현을 할 수 있고, 그것을xp_sqlmaint 확장 저장 프로시저를 통한 T-SQL로부터 호출을 할 수도 있으며 또는 배치 파일이나 응용 프로그램으로부터 sqlmaint 유틸리티를 호출할 수 있다.

     

     

    유지 관리 계획 세우기

     

    유지 관리 계획을 만드는 가장 쉬운 방법은 엔터프라이즈 관리자의 유지 관리 계획 마법사를 이용하는 방법이다. 마법사를 시작하기 위해 관리 노드를 확장하고 데이터베이스 유지 관리 계획을 오른쪽 클릭해서 새 유지 관리 계획 옵션을 선택한다. 첫 번째 화면은 계획을 실행할 데이터베이스를 선택하는 것이다. 데이터베이스는 각각 선택하거나 미리 정의된 multidatabase 옵션(모든 데이터베이스, 모든 시스템 데이터베이스, 모든 사용자 데이터베이스)중 하나를 선택할 수 있다. 이러한 옵션을 사용하는 것은 편리하지만 데이터베이스의 유지관리의 내용이 상황에 따라 크게 다르게 때문에 모든 환경에 적당하지는 않다. 필자는 보통 모든 시스템 데이터 베이스 옵션을 통하여 각 서버에 적용시키는 하나의 계획과, 개발용 이거나 대기중인 서버에서 모든 사용자 데이터 베이스를 사용하는 또 다른 계획을 만든다. 운용환경에선 각각의 중요한 데이터베이스에 맞는 고유한 계획을 작성한다. 그러나 모든 사용자 데이터베이스와 모든 데이터 베이스 옵션은, 해당 서버에 추가되는 모든 새로운 데이터베이스는 계획에 포함된다는 큰 혜택이 있다. 따라서 예를 들면 유지관리계획이 백업에 대한 작업을 포함하고 있을 경우 새로운 데이터베이스의 백업에 대한 내용은 자동으로 백업되므로 신경 쓸 필요가 없다.


    마법사의 다음 몇 단계는 유지 관리 계획을 최적화, 무결성 확인, 데이터베이스 백업과 트랜잭션 로그 백업 등에 관한 업무를 정의하는 것이다. 이러한 각 작업들의 선택 사항을 살펴보자. [그림 2]는 데이터베이스 최적화에 관한 옵션들을 보여준다. 첫 번째 옵션은 인덱스페이지와 데이터의 재 구성에 관한 내용으로 SQL서버의 모든 테이블에 있는 모든 인덱스를 다시 만드는 것을 말한다. 데이터의 단편화에 따라 보통 인덱스를 다시 만드는 것은 데이터와 인덱스 페이지의 사이즈를 축소시킨다. 주기적인 단편화 해소에 따른 인덱스를 다시 만드는 것은 데이터베이스 쿼리 성능을 향상시킨다.

     

    그림 2. 데이터베이스 최적화 옵션

     

    그림 3. 데이터베이스 무결성 검사

     

    왜냐하면 단편화 해소는 SQL서버가 자료를 검색하는데 검색해야 할 데이터베이스의 데이터와 인덱스페이지의 수를 감소시키기 때문이다 당신은 새로운 fill factor를 지정하거나 기존 fill factor로 인덱스를 다시 만들게 할 수 있다. 만약 예를 들어 페이지 여유공간이 10% 남게 선택했다면 sqlmaint 유틸리티가 가 DBCC DBREINDEX (TableName,", 90, sorted_data_ reorg) 명령을 실행하게 된다. 여유 공간의 양은 기존 단편화와 선택한 fill factor에 따라 복구할 수 있다. 인덱스를 다시 만드는 것은 테이블 잠금을 초래하기 때문에 인덱스 를 다시 만드는 것은 데이터베이스의 일반적인 작업과 충돌할 수 있으므로 이 작업은 데이터베이스 사용이 최소로 떨어질 때 수행 해야 할 것이다. 인덱스를 만들거나 재구성하는 것은 최대나 대량 로그 복구 모델을 이용하여 데이터 베이스에 로그되는 동작이다. 따라서 데이터베이스의 인덱스를 재구성 한 후에는 때로 풀 백업의 크기와 근접할 만큼 커진 트랜잭션 로그 백업의 크기를 기대 할 수 있다. SQL서버는 인덱스를 다시 만들 때 자동으로 인덱스 통계 정보를 업데이트하기 때문에, 최적화의 두 번째 옵션(쿼리 최적화 프로그램에 의해 사용되는 통계 정보 업데이트)은 데이터와 인덱스의 재구성을 선택하지 않은 경우만 사용 가능하게 된다. 이 옵션을 선택하는 것은 주기적으로 인덱스 통계 정보를 업데이트하는 통계 자동 업데이트 데이터베이스 기본 옵션을 변경한 경우를 제외하고 대부분 불필요하다. 유지 관리 계획의 부분으로 AUTO_UPDATE_STATISTICS 옵션을 비활성화했을 때 데이터베이스 통계정보를 업데이트하기 권장한다.


    세 번째 옵션은 데이터베이스의 사용되지 않는 공간 삭제에 관한 부분으로 특별한 경우에만 사용해야 하는데, 이는 데이터베이스가 줄어들게 되는 것은 대체로 권장하지 않는 사항이기 때문이다. SQL서버 축소 작업이 일어나면, 서버는 모든 사용중인 페이지들을 데이터베이스의 파일의 처음으로 옮겨야 하고, OS에 여유공간을 돌려주어야 하며, 이는 많은 CPU 자원과 I/O를 야기 할 것이다. 그리고 나서 데이터베이스가 다시 커지기 시작하자마자, SQL서버는 같은 데이터 파일을 다시 확장해야 한다. 그러나 OS에 파일은 더 이상 인접한 공간을 차지하지 않고 있기 때문에 데이터 페이지의 단편화가 생길 것이다. 마법사에서 수행할 수 있는 두 번째 작업은 데이터베이스 무결성 검사이다. [그림 3]과 같이 무결성 탭에서 단순히 데이터만 또는 데이터와 인덱스 모두에 무결성 검사를 할 수 있다.


    “사소한 문제라도 복구 시도”를 선택하면 sqlmaint 유틸리티는 REPAIR_FAST 절을 DBCC CHECKDB 명령에 포함시킨다. SQL서버는 데이터베이스가 단일 사용자 모드일 때만 데이터베이스 복구가 가능하다. 유지 관리 계획은 데이터베이스를 복구 명령이 실행되기 전 단일 사용자 모드로 바꾸길 시도할 것이다. 그렇지만 만약 데이터베이스에 다른 사용자가 있다면 그 명령은 실패할 것이고 작업 스케줄은 에러의 이유에 대한 오류 보고를 작성하며 실행되지 않는다. 데이터베이스가 단일 사용자 모드 상태에서는 DBCC명령과 함께 REPAIR_FAST 가 실행되는 중엔 다른 사용자는 데이터베이스에 접근할 수 없으므로 사용자들은 몇 분간 데이터베이스 작업을 할 수 없을 것이다. 또한 다른 작업 스케줄도 sqlmaint가 다중 사용자 모드로 되돌려 놓기 전까지는 작업을 수행할 수 없다. 그러므로 BOL에서 조차 복구 옵션에 관하여 권하고 있다 할지라도, 데이터베이스의 사용패턴과 유지 관리 작업 스케줄 실행 시점에 대하여 데이터베이스에 사용자가 없다는 것을 확신하거나 DBCC 명령의 오류가 보고되었다면 수동으로 실행시킬 필요가 있지를 평가할 필요가 있다. 백업 실행 전에 검사 수행을 선택해서 전체 백업이나 로그 백업 전에 DBCC CHECKDB 명령 실행을 수행할 수 있다. 만약 DBCC에서 문제가 발생한다면 sqlmaint는 백업을 수행하지 않을 것이다. 이 작업의 수행은 오류를 포함한 데이터 베이스의 백업을 방지하도록 설계되었으나 인덱스상의 간단한 오류나 sqlmaint가 단일 사용자모드로 전환할 수 없는 상황에서도 백업이 불가능하며 데이터는 위험에 놓이게 된다. 만약 이 옵션을 선택하면 무결성 검사에 대한 작업의 상태를 모니터링 해서 데이터베이스에 대하여 필요하다면 즉시 수정하거나 백업을 다시 할 수 있어야 한다. 이 옵션에 대한 또 다른 내용은 무결성 검사의 빈도 부분이다. 무결성 검사는 많은 리소스를 필요로 하며, 그 결과 테이블의 잠금과 그로 인하여 응용프로그램의 데이터베이스 접근을 방해하고 데이터베이스와 서버의 성능에 영향을 미칠 수 있다. 데이터베이스의 사용률이 최저로 떨어졌을 때 본 내용을 수행 하여야 한다. 불행하게도 마법사는 전체 백업 전 무결성 검사를 수행하도록 선택하게 되어 있지 않지만 로그 백업 전에는 아니다. 이 제한 사항으로 이 옵션은 로그백업을 빈번하게 해야 하는 데이터베이스로는 부적당하다. 나는 매시간 무결성 검사와 로그백업을 수행하는 대용량 데이터베이스에서 이 옵션을 사용하는 경우를 본 적이 있다. DBCC 명령을 수행하는데 20분이 걸렸으며 따라서 데이터베이스 서버는 운영시간에 1/3 정도를 무결성 검사에 자원을 사용했다.


    이러한 문제를 다루기 위해, 데이터베이스 관리자들은 각각의 데이터베이스에 대한 무결성 검사와 함께 전체백업을 하는 경우와 로그백업을 수행하는 작업 등의 두 개의 유지관리 계획을 세워야 한다. 이 기사를 연구하는 동안 또 다른 업무 범위를 발견했다. 만약 마법사에서 백업이전에 무결성 검사를 선택하면 전체 백업과 로그백업에 대한 작업스케줄은 아래 두 개의 스위치를 포함하여 xp_sqlmaint 를 호출한다.

     

    -BkUpOnlyIfClean -CkDB

     

    만약 마법사가 만들어준 트랜잭션 로그 백업에 대한 작업 스케줄의 스위치를 수동으로 지우면 로그백업이전에는 무결성 검사는 이루어지지 않을 것이나 여전히 전체 백업은 수행될 것 이다.

    그림 4. 엔터프라이즈 관리자에서 작업 정보

     

    그림 5. 백업 옵션

     

    트랜잭션 로그백업 작업을 [그림 4]에서 보는 바와 같이 엔터프라이즈 관리자의 관리노드 ? 서버에이전트-작업 에서 찾을 수 있다. 만약 마법사가 작업을 만든 후 수동으로 작업 명을 바꾼 적이 없다면, 트랜잭션 로그백업의 이름은 유지 관리 계획 “PlanName” 의 트랜잭션 로그 백업일 것이다. 마이크로소프트 기사 "BUG: DB Maintenance Plan Cannot Be Modified to Include/Exclude Integrity Checks Before Backups"(http://support.microsoft.com/?kbid=264194)에 설명되어 있는 버그에 대한 문제를 수정하기 위해 같은 기술을 사용할 수 있다. 이 버그는 백업이전에 기존 무결성 검사 작업이 수정되는 것을 막아주기 때문에 계획을 만들 때 옵션을 선택하는 데만 집중할 수 있도록 해준다. 옵션 스크린과 버그로 인한 수행 결과로부터의 주어진 한계에 대한 최적의 선택은 아마도 작업 스케줄 명령에 이러한 두 스위치를 수동으로 관리하는 것일 것이다.

     

     

    데이터베이스 백업 옵션

     

    무결성 검사를 구성하고 나면 마법사는 가장 중요한 데이터베이스 유지관리 계획인 전체 데이터베이스와 트랜잭션 로그 백업을 설정하게 된다. 백업 옵션은 [그림 5]에서 보는 바와 같이 전체 백업과 로그 백업으로 나뉜다. 유감스럽게도 유지관리 계획은 차등 백업은 지원하지 않는다. 파일이나 파일그룹 백업을 지원하지 않으므로 유지관리 계획의 부분으로서 전체 백업은 파일이나 파일 그룹 백업을 사용하는 장점을 가지는 큰 규모의 데이터베이스에 항상 유리한 것은 아니다. 완료 시 백업 무결성 확인 체크박스를 선택함으로써 백업 셋이 완전하고 읽기 가능한지를 확인하도록 유지관리 계획을 구성할 수 있다. 만약 매체로서 테이프 드라이브를 사용하는 경우 특정 백업경로를 선택할 수 있으나 성능상의 이유로 SQL 서버 백업을 로컬 하드디스크로 바로 하고 하드 디스크의 백업 파일을 테이프로 하기를 권장한다. 내가 좋아하는 sqlmaint 유틸리티 옵션은 자동으로 오래된 파일을 지우는 작업인데 수분에서 수개월까지 기간을 두어 보존할 수 있게 할 수 있다.


    필자는 유지 관리 계획의 트랜잭션 로그 백업을 실행시키며 몇몇의 문제를 발견했다. 만약 단순 복구 모델을 사용하여 로그 백업을 포함시킨다면 마법사는 그 동작이 어긋난다는 경고를 주지 않고 작업 스케줄은 실패 할 것이다. ?SQL 서버 2000 만 해당 SQL 7.0은 버그를 가지고 있는데 이는 기사 "BUG: Sqlmaint Does Not Report Error on BACKUP LOG When Truncate Log on Checkpoint is Set" 로 마이크로소프트 웹사이트 (http://support.microsoft.com/?kbid=242500) 에서 확인이 가능하다. 이는 실패로 이 에러를 보고 하지 않는다. SQL 서버 2000으로 업그레이드 할 때 각 에러 없이 실행된 작업들이 에러를 발생한다면 이러한 문제점에 대해서는 수정할 필요가 있다. 이 버그는 "BUG: Expired Transaction Log Backups May Not Be Deleted by Maintenance Plan"의 문서의 내용과 다소 관계가 있다. (http://support.microsoft.com/?kbid=303292). 문서에 따르면 만약 유지관리 계획이 여러 개의 데이터베이스에 대한 로그 백업과 적어도 하나 이상의 데이터베이스를 단순복구 모델로 실행할 때 sqlmaint 유틸리티는 모든 데이터베이스에 대해 기간이 지난 데이터베이스의 백업들을 삭제하지 못한다. 만약 이 문제가 발생하면 반드시 실행 계획에 속해있는 모든 데이터베이스를 최대나 대량로그 복구모델을 사용하거나 제 2의 단순 복구 모델을 사용하는 유지관리 계획을 세워야 한다. 트랜잭션 로그 백업은 계획의 구성에 있어서 제외시킬 필요가 있다.

     

     

    Monitoring and Notifications

    그림 6. 리포팅 옵션 정의

     

    [그림 6]에서 보는 바와 같이 보고서 탭은 몇몇의 유지관리 계획에 대한 보고옵션을 정의하도록 한다. 보고 문서는 텍스트 파일로 출력이 가능하고 만료 시 SQL서버가 자동으로 삭제한다. sqlmaint 유틸리티는 HTML파일로도 출력할 수도 있으나 이 보고서 탭에서는 그 내용을 선택할 수 없다. 대신 스케줄 작업에서 xp_sqlmaint를 호출할 때 HtmlRpt 과 DelHtmlRpt 스위치를 사용하여 수정할 필요가 있다. 또한 로컬이나 원격 서버 위에 sysdbmaintplan_history 테이블에 로그에 대한 기록을 남기도록 선택할 수 있다.

     

    그림 7. 데이터베이스 유지 관리 계획 기록, 대화상자

     

    유지관리계획실행에 대한 기록을 검토하는 가장 쉬운 방법은 [그림 7]에서와 보여지는 것과 같이 데이터베이스의 유지관리 계획 기록 대화상자를 확인하는 것이다. 이 대화상자는 엔터프라이즈 관리자의 데이터베이스 유지관리 계획의 노드에서 오른쪽 클릭해서 유지관리 계획 기록을 선택한다. 이 대화상자는 다양한 검색 필터를 제공하므로, sysdbmaintplan_history table 의 수많은 열들을 드릴 다운하는 것은 쉽다. 또한 고객들이 보기 위한 리포트를 작성할 수 있다. 만약 여러 개의 서버를 관리한다면 하나의 서버에 있는 sysdbmaintplan_history테이블에 기록되도록 구현 할 수 있다. 또 다른 옵션은 여러 개의 링크된 서버의 sysdbmaintplan_history 테이블을 읽기 위해 UNION 절을 사용하는 뷰를 만드는 것이다. 기록 테이블과 sqlmaint 텍스트 보고서는 작업스케줄 기록보다 실패 사유에 대하여 좀 더 자세한 내용을 제공한다. 엔터프라이즈 관리자에서 유지관리 작업에 대한 실패를 발견했을 때 나는 유지 보수 관리 기록을 확인하는 것이 더욱 좋다고 생각하는데 그 이유는 작업스케줄 기록은 대부분 sqlmaint.exe failed" 이라는 오류보고 만을 가지고 있기 때문이다.


    이런 중요한 환경에서는 무엇이 어떻게 잘못되었는지를 기록 테이블이나 계획 오류 보고만을 주기적으로 살펴보는 그 누군가에게 의존을 할 수는 없을 것이다. 만약 백업이 실패했다면 그에 대한 해결책을 만들기 위해 즉시 통보 받기를 원할 것이다. 마법사나 유지 관리 계획 화면에서 유지 관리 계획의 실패의 통보를 설정 할 순 없다. 이런 보고를 정의하기 위해선 스케줄 작업을 수정할 필요가 있다. SQL는 유지 관리 계획이 변경되었을 때 작업스케줄과 작업명령을 수정 하지만 작업 보고 부분은 그대로 유지된다. 작업 스케줄은 이메일, 호출기, Net Send 의 운영자 옵션을 지원한다. 만약 유지관리 유지관리계획을 삭제하거나 나중에 다시 생성이 되면, 이러한 통보 부분은 다시 작성을 할 필요가 있다.


    SQL서버 유지관리 계획은 데이터베이스 유지관리 요구를 정의하고 제공하는 강력하고 유연한 메커니즘이다. 최적화된 유지관리 계획을 생성함으로 인해, 당신은 데이터베이스를 유지관리하고 모니터링 하는데 소비될 많은 시간을 줄일 수 있을 것이다.

     

    Posted by redkite
    , |

    개발자를 위한 튜닝 가이드 - 쿼리 디자인

     

    쿼리 디자인

    번호 수칙 체크
    1 SELECT는 필요한 결과값만을 요구하는가?
    2 적절한 WHERE조건을 사용하는가?
    3 COUNT(컬럼명) 대신 COUNT(*)을 사용하는가?
    4 커서 및 임시 테이블의 내용을 최대한 자제하는가?  
    5 VIEW의 총 사용을 줄였는가?  
    6 저장 프로시저를 사용하는가?  
    7 저장 프로시저를 적절하게 리컴 파일 하며 사용하는가 ?  
    8 작명 된 저장 프로시저 SP외의 접두어를 사용하는가 ?  
    9 모든 개체의 소유자는 DBO로 지정하며 생성했는가 ?  
    10 데드락이 발생하는 부분을 라이브락 형태로 변경했는가?  
    11 SET NOCOUNT ON을 사용하는가?  
    12 실무 사례: 저장 프로시저 관리 방법  

     

    스티브 맥코넬이 이런 말을 했습니다. 
    뛰어난 디자이너는 습득한 지식을 사용하지 않는 것과 그 지식을 처음부터 확보하지 못한 것을 동일하게 봅니다. 
    이말 뜻을 다음과 같이 해석하고 싶습니다. 여러분은 쿼리 분석기의 기능들이 어떤 것이 있고, 단축키가 메뉴우측에 작게 표시되어 있다는 것을 대부분 알고 있습니다. 하지만 잘 사용하지는 않고 있을 것입니다. 라고 말입니다. 그래서 먼저 단축키와 그 사용법에 대해 안내하는 시간을 우선 가지도록 하겠습니다. 
    다음을 실습해보고 자세한 것은 표를 참조합시다

     

    -- CTRL + E , F5 

    -- 실행하기
    ----------------------------------------------------------------
    use pubs 
    go 

    select * from titles 

    -- CTRL + T => 결과 Text로 보기 

    select * from titles 

    -- CTRL + D => 결과 Text로 보기 

    select * from titles 

    -- CTRL + K => 실행계획 보기 

    select * from titles 

    -- F8 => 개체브라우저 보이기/감추기 

    -- CTRL + R => 결과창 보이기/감추기 

    -- 그외 CTRL + C , CTRL + V , CTRL + X 

    -- CTRL + SHIFT + C => 주석달기 

    select * from titles


     

    다음의 표를 참조합시다.

      없음 Shift + Ctrl+ Alt+ Shift+Ctrl+
    A     전체 선택    
    B     중간 구분선 선택    
    C     복사   주석 달기
    D     표 형태로 결과 표시 데이터베이스 선택  
    E     실행    
    F     찾기   파일로 결과 저장
    G          
    H     교체    
    I     인덱스 튜닝마법사    
    J          
    K     실행 계획 보기    
    L     예상 실행 계획 보기   선택 내용을 소문자로
    M          
    N     새 쿼리 윈도우    
    O     연결    
    P          
    Q          
    R     결과창 보이기/감추기   주석제거
    S     저장    
    T     텍스트로 결과 표시    
    U         선택 내용을 대문자로
    U     선택 내용을 대문자로    
    V     붙여넣기    
    W          
    X     자르기    
    Y     다시하기    
    Z     취소    
    F1 도움말 선택 내용을 도움말로 보기      
    F8 객체 브라우저보이기감추기        

    주요 단축키 사용 안내 입니다.

     

    수칙1.SELECT는 필요한 결과값만을 요구하는가?

     

    select title , price from titles where title_id = 'BU1032'

     

    Select 하는 내용도 필요한 항목만을 가지고 오도록 되어 있어서 리소스가 전혀 낭비되지 않고 있습니다.

     

    select title , price from titles

     

    필요한 칼럼을 가져오기는 하지만 불필요한 전체 행(Row)들을 가져오고 있습니다.

     

    select * from titles

     

    불필요한 칼럼정보, 행(Row) 데이터를 가져오고 있습니다.

     

    따라 하기 - 3개의 쿼리를 한번에 실행하기 

    1.3개의 쿼리를 한 Session에서 실행하여 결과3개를 동시에 살펴봅니다.



    2.결과 값으로 출력되는 데이터량의 차이를 확인합니다. 어느 쿼리가 가장 간결한 결과를 반환합니까? 
    ( 반드시 꼭 필요한 결과만 반환하게 하는 것이 좋습니다. 
    select title , price from titles where title_id = 'BU1032' 가 적절합니다.)

     

    수칙2.적절한 WHERE 조건을 사용하는가?

     

    인덱스란 데이터를 빨리 찾기 위해서 사용됩니다. 인덱스가 없다면 특정데이터를 찾기 위해서 모든 데이터페이지를 검색(Table Scan)해야만 합니다 그에 비해 인덱스가 존재하고 그 인덱스가 사용되는 것이 효과적이라면 SQL서버는 해당 인덱스 페이지를 사용하여 쉽게 데이터를 가져올 수 있는데 이를 인덱스 검색(Index Seek)이라 합니다.


    그러나 이렇게 인덱스가 있더라도 이를 사용 불가능하게 하는 나쁜 쿼리가 있으니 이는 검색조건에서 불필요하게 칼럼이 변형된 경우입니다. 다음의 여러 나쁜 예를 좋은 예와 비교해 봅시다.


    SARG(Search Argument)란 쿼리가 반환하는 결과를 제한하기 위하여 옵티마이저가 인덱스와 결합해서 사용할 수 있는 쿼리 내의 조건절을 말하는데 다음의 형태를 가집니다.

     

    컬럼 연산자/변수

     

    옵티마이저가 쓸모 있게 변환하는 것은 CTRL+K 실행 계획 상부 표시에서 관찰할 수 있습니다.

     

    set showplan_all on 

    select * from authors 
    where au_lname like 'Ma%' 

    -- OBJECT:([pubs].[dbo].[authors].[aunmind]), SEEK:([authors].[au_lname] >= 'Ma' AND 
    [authors].[au_lname] < 'MB'), WHERE:(like([authors].[au_lname], 'Ma%', NULL)) 
    ORDERED FORWARD 

    -- set showplan_all off

     

    따라 하기 

    1.인덱스 찾기(Index Seek)를 확인합니다.



    2.다음과 같이 약간의 조건절(where) 변형만으로 인덱스페이지가 사용되지 않음을 확인합니다.



    3.그렇다면 조건절(where) 변형하고 싶을 땐 어떻게 해야 할까요?



    4.다음 예제도 복습해 봅시다.





    5.항상 실행 계획을 참조하여 재차 쿼리를 확인해야 합니다.

     

    [참고] 쿼리 계획 은 다음의 몇 가지 단계로 이뤄집니다. 

    1. 평범한 계획을 식별 
    2. 획을 단순화 - having를 where로 != @param을 < @param OR > @param으로 변환하는 것 같은 작업을 수행합니다 
    3. 로드 한다 - 쿼리 옵티마이저가 인덱스와 컬럼 통계, 다른 지원정보를 로드한다 
    4. 근거하여 계획들을 평가한다 - 실행하는 비용이 충분히 저렴하다고 생각될 때 그 계획을 실행하도록 내놓는다 
    5. 병렬화를 위해 최적화한다 - SMP

     

    수칙3.COUNT(컬럼명) 대신 COUNT(*)을 사용하는가?

     

    COUNT(*) 와 COUNT(컬럼명)의 차이는 중요합니다. COUNT 하는 해당 테이블 컬럼에 NULL 값을 포함하고 있다면 이 두 예제는 서로 다른 결과를 반환합니다. COUNT(컬럼명)은 그룹에 포함된 각 행을 평가하여 NULL이 아닌 값의 개수를 반환합니다. COUNT(*)는 NULL 값과 중복된 값을 포함한 그룹의 항목 개수를 반환합니다.


    일반적으로, COUNT(컬럼명)을 사용하여 특정한 컬럼의 행 개수를 세는 것보다 COUNT(*)을 사용하여 옵티마이저가 행의 개수를 반환하는 최상의 방법을 선택하도록 해주는 것을 더 선호하는 방식이다.

     



    [참고] NULL을 처리하는 방법 

    use pubs 
    go 

    -- 돈 받고 파는 책을 출력하세요 

    select * from titles where price is not null 

    -- 비매품인 책을 출력하세요 

    select * from titles where price is null 

    -- 비매품책을 제외한 모든 책의 평균가격? 

    select avg(price) from titles 

    -- 비매품책을 0원으로 두고 계산한 평균가격? 

    select avg(isnull(price,0)) from titles 

     

    [유용한 관용구]


    칼럼의 중복 행의 수를 찾아봅시다 

    use pubs 
    go 

    -- 중복 칼럼이 각각 몇 개 항목인지를 찾아보자 
    -- type별로 몇 개의 책이 있을까? 

    select type,count(*) as [중복 행의 수] 
    from titles group by type having count(*)> 1

     

    수칙4.커서 및 임시 테이블의 내용을 최대한 자제하는가?

     

    결론부터 말씀 드리자면 커서보다는 임시테이블이 임시테이블보다는 테이블 변수를 사용하는 것이 성능에 보탬이 됩니다. 단 SQL2000에서만 테이블 변수가 가능합니다.


    커서는 내부적으로 임시 테이블을 사용하기 때문에 임시테이블을 쓴다고 부하가 더 발생하진 않습니다. 오히려 커서의 부가적 기능 때문에 서버 자원을 더 낭비하게 됩니다. (커서로 할 수 있는 건 임시테이블이나 테이블 변수로도 모두 처리가 가능합니다.)

     

    따라 하기 - 다음은 테이블 변수를 사용하여 기존 커서를 대체하는 것을 구현했습니다. 

    1.훌륭하게 커서를 대신하는 문장입니다.



    CTRL + K로 확인하면 테이블 변수로 사용할 경우 실제 테이블에 잠금을 전혀 걸지않는 것을 알 수 있습니다(중요) 그와 반대로 커서를 사용할 경우 프로시저 시작부터 끝까지 지속적으로 사용 부분을 계속해서 잠그고 있어서 다른 작업들이 대기해야 되는 문제가 생깁니다.

     

    수칙5.VIEW의 총사용을 줄였는가?

     

    VIEW는 보안과 편리성에 관련된 이슈를 다루는 데 있어 최고입니다, 
    그러나, 일반적으로 보안상에 이슈를 제외한 경우에는 불필요한 부하가 가중될 수 있고 많은 경우에 더 불필요한 데이터를 반환합니다 예를 들면 VIEW에서 10개를 가져오고 거기에 WHERE 조건을 붙여서 7개만 가져오는 경우가 그렇습니다.

     



    select lastname,firstname from employees VS select * from EmployeesView 중간 단계가 있는 쪽이 효율이 떨어집니다.

     

    수칙6.저장 프로시저를 사용하는가?

     

    저장 프로시저는 복잡한 SQL문을 단순화 시켜주고, 보안 문제를 해결해주며 더 나아가 빠른 성능에 매개변수,출력매개변수,리턴 값을 사용할 수 있습니다.

     

    저장 프로시저의 역할 7가지 

    1. 데이터 무결성의 시행 
    2. 복잡한 비즈니스 규칙과 제약의 강화 
    3. 캡슐형 설계 
    4. 유지보수 
    5. 네트워크 트래픽 감소(오고 가는 긴SQL 구문을 축소) 
    6. 보다 빠른 실행(컴파일을 하지 않습니다) 
    7. 보안강화

     

    저장 프로시저의 생성과 반복사용 시 발생하는 일

     

    제작
    1. 구문분석 
    2. 표준화 
    3. 보안 점검(프로시저 생성권한) 
    4. 저장(syscomments)
    첫 번째 실행 시
    1. 보안 점검(프로시저 실행 권한) 
    2. 최적화 
    3. 컴파일과 이에 따른 실행계획을 캐쉬에 저장 
    4. 실행
    반복해서 실행 시
    1. 캐쉬에 실행 계획 있을 때는 그대로 실행 
    2. 캐쉬에 실행 계획이 없을 때는 첫 번째로 저장 프로시저 실행하는 것과 동일

     

    쿼리는 한번만 실행할 때는 일반 SQL이 훨씬 간단합니다. 그러나 반복적으로 실행되면 저장 프로시저가 월등히 빠르고 편리합니다.

     

    수칙7.저장 프로시저를 적절하게 리컴 파일 하는가?

     

    데이터가 변화하면(인덱스를 추가하거나 인덱스된 열의 데이터를 변경하는 등의 작업 수행 시) 그에 걸맞게 실행계획도 변화해 갑니다. 그에 대처하기 위해서 다음과 같은 리컴파일 방법를 제공합니다. 

    저장 프로시저 리컴 파일 모드에는 다음의 3가지가 있습니다.

     

    CREATE PROCEDURE [WITH RECOMPILE] 
    EXECUTE [WITH RECOMPILE] 
    sp_recompile

     

    CREATE PROCEDURE [WITH RECOMPILE]

    는 SQL SERVER가 이 저장 프로시저의 계획을 캐시하지 않기 때문에 이 저장 프로시저가 실행 할 때 마다 다시 컴파일 됩니다(실행 속도가 느려짐).

     

    EXECUTE [WITH RECOMPILE]

    는 지금 이순간만 리컴파일 하고 다시 저장 프로시저 실행하면 예전 실행 계획대로 작동하는 것입니다. 제공하는 매개 변수가 불규칙하거나 저장 프로시저를 만든 다음 데이터가 많이 변경되었을 경우 이 옵션을 사용합니다.

     

    sp_recompile

    는 저장 프로시저가 다음에 실행될 때 첫 실행처럼 컴파일되고 실행되도록 하는 것입니다.

     

    [문서화되지 않은 DBCC 명령어]

    -- pubs 데이터베이스의 모든 저장 프로시저를 재컴파일 해보자 

    select db_id('pubs') 

    dbcc flushprocindb(5) 

    -- 모든 인덱스를 재구축한다 
    -- 관리자가 사용할 경우 엄청난 시간이 소요될 수 있습니다 

    dbcc dbreindexall('pubs')


     

    수칙8. 저장 프로시저 작명 시 SP외의 접두어를 사용한다.

     

    시스템 저장 프로시저는 master 데이터베이스내에서 sp_라는 접두어로 시작하는 것이 좋으며 모든 데이터베이스에서 실행될 수 있습니다. 각 사용자 데이터베이스에서는 다른 접두어를 사용하는 것이 보기에도 좋고 알아보기에도 수월합니다.


    또한 시스템 저장 프로시저는 어느 데이터베이스에서 수행하건 해당 데이터베이스의 내용을 참조합니다.

     

    따라하기 
    일반sp_ 저장 프로시저를 시스템sp_ 저장 프로시저로 만들어 봅니다. 

    1. 사용자 정의 저장 프로시저는 master데이터베이스에 존재하더라도 master내용만 참고합니다.



    2.그러나 다음과 같이 시스템 저장 프로시저화 한다면



    3.부연하자면 모든 데이터베이스에서 사용하는 프로시저의 경우 sp_로 시작하게 작성한후 sp_MS_marksystemobject로 시스템 프로시저화 작업을 하는게 필요합니다. 이 내용은 엄격하게 구분되서 실행되는 것이 혼란을 줄일 수 있습니다.

     

    수칙9. 모든 개체의 소유자는 DBO 이다

     

    소유자가 다르면 복잡한 소유권 체인문제가 발생합니다.



    lucia가 테이블의 소유자 입니다. lucia는 뷰를 만들었는데 maria에게 뷰를 볼 수 있게 했습니다. maria는 이를 Pierre가 볼 수 있게 했는데 Pierre는 Maria가 만든 뷰를 select권한을 받았음에도 불구하고 실행이 안됩니다. 이는 소유권 체인이 중간에 분실 됐기 때문입니다. 불필요한 이런 시스템은 시스템의 성능저하를 가져다 줍니다. 모든 소유자는 dbo로 통일하는 것을 권장합니다.

     

    따라 하기 - 소유자를 dbo로 바꿔보자 

    1.소유자를 dbo로 바꿀 때는 다음의 저장 프로시저를 사용하면 됩니다.



    2.추가로 시스템테이블을 업데이트하는 방법을 통해 데이터베이스 차원에서 소유자를 바꾸는 방법도 있으며 커서를 사용하는 방법도 존재합니다.

     

    [참고] 소유자가 dbo가 아닌 객체를 출력해봅시다. 

    select name from sysobjects 
    where uid <> user_id('dbo')

     

    수칙10. 데드락이 발생하는 부분을 라이브락 형태로 변경했는가?

     

    데드락이란 라이브락과 반대되는 개념입니다. 둘 이상의 트랜잭션이 서로가 실행해야 될 내용을 이미 잠그고 있어 마치 교차로에서 서로 엉켜 꼼짝할 수 없는 상황을 의미 합니다. 이를 해결하기 위한 SQL서버의 노력은 한쪽을 일방적으로 취소 시키는 것인데 이는 시스템의 성능저하로 나타납니다. 이를 해결하기 위한 가장 좋은 방법은 일방통행 방식으로 변경하는 것입니다. 이것이 라이브락 입니다.

     

    따라 하기 

    1.우선 준비를 위해 테이블을 만들고 데이터를 넣습니다.



    2.창을 두 개 열어서 동시에 실행합니다. CTRL+TAB으로 잽싸게 창을 바꿔서 실행해 봅니다.



    3.위의 데드락의 가장 바른 해결방법은 순차적인 라이브락 형태로 변경하는 것입니다.



    수칙11. SET NOCOUNT ON을 사용하는가?

     

    불필요한 메시지가 네트워크 트래픽을 낭비하고 있습니다. 특히'몇 개 행이 적용 되었습니다' 같은 메시지가 그런 대표적인 예입니다.

     

    따라 하기 

    1. set nocount on을 실행하고 쿼리를 실행합니다.



    프로시저를 작성할 때도 set nocount on과 같은 환경설정은 먼저 실행해 두고 프로시저를 작성하면 환경이 저장된 채로 프로시저가 제작되므로 편리합니다.

     

    수칙12.실무 사례: 저장 프로시저 관리 방법

     

    저장 프로시저 관리방법



    --------------------------- 
    -- 객체이름 : 
    -- 파라미터 : 
    -- 제작자 : 
    -- 이메일 : 
    -- 버젼 : 
    -- 제작일 : 
    -- 변경일 : 
    -- 그외 : 
    ---------------------------

    use 데이터베이스명
    -- 저장 프로시저는 use 데이터베이스명 문과 같이 써두어야 어디 소속인지 명확히 파악이 가능합니다. 
    go 

    -- 소스 

    create proc dbo.저장 프로시저명 
    -- 소유자가 명확하게 dbo로 지정 되 있어야 성능 향상이 이뤄집니다. 컴파일 잠금 시간 대폭 감소 
    as 
    begin 
    -- 가장 바깥쪽의 begin end 및 불필요한 begin end문은 과감히 생략합니다.(소스만 길어짐) 

    end 

    -- 실행예제 

    exec 데이터베이스명.dbo.저장 프로시저명 
    -- 데이터베이스 이름까지 명시해야 오브젝트 참조에서 발생할 수 있는 불 명확성을 줄여줌으로 바람직합니다

     

    Posted by redkite
    , |

    성능 향상을 위한 파티션 테이블 사용은 필수

     

    필요에 의해 파티션 테이블을 생성하는 경우 무엇을 최우선으로 고려하는가? 파티션 테이블은 보관 주기 관리와 성능 향상을 위해 이용한다. 그런데 파티션 테이블의 존재는 물론이고 이것의 사용에 따른 혜택조차 모르는 경우가 비일비재하다. 파티션 테이블을 사용함으로써 얻을 수 있는 혜택을 정확히 이해하지 못하기 때문에 사용을 꺼릴 수 밖에 없다. 지난 호의 파티션 테이블과 보관 주기 관리에 이어 파티션 테이블을 이용한 성능 향상을 확인해 보자.

     

     

    배치 작업으로 아직도 밤을 세우는가


    대부분의 웹사이트 관리자는 사이트 운영 중에 데이터베이스를 액세스하는 일 배치 작업 또는 월 배치 작업으로 밤을 세우는 경우가 많다. 어떤 사이트는 당연한 일로 여기며 순번을 정해 작업을 하는 경우도 있다. 그렇다면 대용량의 일 배치 작업 또는 월 배치 작업으로 밤을 세우는 것은 당연한 일인가. 맞을 수도 있고 틀릴 수도 있다. 하지만 분명한 것은 배치 작업에 사용되는 테이블을 파티션 테이블로 구성함으로써 배치 작업의 성능을 10배 이상 향상시킬 수 있다는 것이다. 

    월 배치 작업은 전 달의 데이터를 액세스하여 우리가 원하는 리포트 또는 여러 가지 유형으로 데이터를 추출하는 것을 의미한다. 예를 들어, 10개월 보관 주기의 한달 치 데이터가 30GB인 테이블이 존재한다고 가정하자(전체 테이블의 크기는 300GB). 이러한 대용량 테이블은 어떤 방식으로 액세스하여 월 배치 작업을 수행해야 할까? 해당 테이블로부터 1개월 데이터를 액세스하는 방법에는 두 가지가 존재한다. 첫 번째 방법은 테이블을 전체 액세스하여 최근 1개월 데이터를 선택하고 나머지 데이터는 버리는 방법이다. 두 번째는 인덱스를 액세스하여 1개월의 데이터만 액세스하는 것이다. 

    그럼 먼저 전체 데이터를 액세스한 후 최근 1개월 데이터만 선택하는 배치 작업의 형태를 보자. 전체 데이터를 액세스하려면 전체 테이블을 액세스해야 한다. 즉 300GB의 테이블을 모두 엑세스해야 한다. 300GB의 데이터를 모두 액세스한 후 270GB는 우리가 원하는 데이터가 아니므로 버리고 나머지 1개월 데이터인 30GB로 원하는 작업을 수행하게 된다. 하나의 작업에서 300GB의 데이터를 한번에 액세스하는 것은 결코 쉽지 않다. 300GB의 테이블을 전체 액세스하는 것은 해당 시스템에 엄청난 성능 저하를 발생시킨다는 것을 대부분의 사람들이 알고 있을 것이다. 

    또한, 우여곡절 끝에 300GB를 액세스했다고 하더라도 270GB를 버린다는 것은 매우 비효율적이다. 데이터는 추출하는 것도 성능을 저하시키지만 버리는 것 또한 성능 저하를 발생시킨다. 버리는데도 확인 작업이 수행되기 때문이다. 이처럼 전체 데이터를 액세스하는 것은 추출할 때나 버릴 때 모든 작업에서 성능 저하를 발생시킨다. 그러므로 이 같은 작업을 수행한다면 원하는 시간에 작업을 종료할 수 없게 될 것이다. 하루가 아닌 이틀 또는 삼일 밤을 세워야 할지도 모르는 것이다. 

    두 번째로 인덱스를 이용한 한달 치 데이터만 액세스하는 예를 확인해 보자. 인덱스를 생성한 후 해당 인덱스를 이용하여 원하는 월의 데이터를 액세스한다면 인덱스는 테이블보다 크기가 작으므로 30GB보다 작겠지만 테이블은 30GB를 액세스해야 한다. 이는 인덱스를 액세스한 후 테이블을 액세스해야 하기 때문이다. 이 같은 경우 랜덤 액세스가 30GB 발생하게 된다. 

    랜덤 액세스는 데이터가 저장되어 있는 개개의 블록을 액세스하는 방식을 이용하며 인덱스를 액세스한 후 테이블을 액세스하는 경우에는 랜덤 액세스가 발생하게 된다. 인덱스를 액세스한 후 테이블을 액세스하는 경우 하나 하나의 데이터를 각각 액세스하게 된다. 이러한 랜덤 액세스로 인해 DB 관련 서적 대다수가 인덱스를 이용할 경우 해당 테이블의 3%~5% 미만을 액세스해야 한다고 이야기한다. 위의 경우는 인덱스 액세스 후 테이블을 액세스하는 비율이 10%에 해당하므로 인덱스를 이용해서는 안될 것이다. 물론, 데이터가 많으면 많을수록 랜덤 액세스에 대한 비율은 더 낮아져야만 성능을 보장할 수 있다. 

    결국, 위의 경우 인덱스를 이용한 테이블을 액세스하게 되면 더 이상 성능을 보장할 수 없다. 전체 테이블을 모두 액세스하는 경우보다 성능이 저하될 수 있다는 것을 명심하길 바란다. 

    그렇다면 어떻게 작업을 수행해야 할까? 이에 대한 해답은 파티션 테이블이다. 월별로 파티션 테이블을 생성한다면 월별로 데이터를 저장하게 된다. 이렇게 테이블을 구성한다면 원하는 파티션만 전체 스캔할 수 있다. 파티션 전체 스캔은 파티션 별로 데이터가 저장되므로 원하는 파티션만 액세스할 수 있다. 배치 작업을 수행하기 위한 해당 월만을 저장하는 파티션이 존재하게 되며 해당 월 파티션만을 전체 스캔할 수 있게 된다. 이와 같이 수행한다면 한달 치 데이터인 30GB만 액세스하므로 액세스 후 버려지는 데이터도 없어진다. 또한 인덱스를 이용하지 않고 원하는 파티션 전체만을 스캔하기 때문에 랜덤 액세스도 발생하지 않는다. 

    이 같이 배치 작업은 파티션 테이블의 이용으로 최적화가 가능하다. 아울려 파티션 구조를 어떻게 구성하는가에 따라 일 배치 작업도 최적화된 액세스를 수행할 수 있을 것이다. 정규적인 대용량 배치 작업의 해결 방법은 파티션 테이블에 존재한다고 해도 과언이 아니다. 


    파티션 테이블과 피라미드 원리


    파티션 테이블은 전체 데이터를 기준 컬럼으로 분리하여 별도로 저장하는 테이블을 의미한다. 이러한 이유에서 파티션 테이블은 피라미드 원리와도 같다. 그렇다면 피라미드 원리라는 것은 무엇인가? 1,000개의 벽돌을 바닥에 깔고 피라미드를 쌓는다고 가정하자. 그러면 1,000개의 벽돌에 의해 만들어지는 피라미드의 높이가 결정될 것이다. 이번에는 피라미드의 바닥을 1,000개의 벽돌로 쌓는 것이 아닌 100개의 벽돌로 10개의 피라미드를 쌓는다고 가정해 보자. 

    전체 바닥에 사용된 벽돌은 1,000개의 벽돌로 앞서 언급한 경우와 동일하다. 하지만 바닥의 벽돌 개수에 의해 피라미드의 높이는 달라진다. 1,000개의 벽돌로 쌓은 한 개의 피라미드와 100개의 벽돌로 쌓은 10개의 피라미드 중 어떤 것이 더 높을까? 당연히 1,000개의 벽돌로 쌓은 피라미드가 훨씬 높을 것이다. 

    파티션 테이블도 이와 별반 다르지 않다. 각각의 데이터를 파티션으로 구성하기 때문에 해당 테이블의 인덱스 또한 파티션으로 구성할 수 있으며 이럴 때 인덱스는 피라미드 원리가 적용된다. 

    예를 들어, 파티션 테이블이 아닌 일반 테이블에서 하나의 데이터를 추출하려면 1,000개의 벽돌로 피라미드를 쌓는 것처럼 맨 위에서 원하는 벽돌을 찾아가야 한다. 하지만 테이블과 인덱스를 파티션으로 구성한다면 100개의 벽돌로 만든 10개의 피라미드가 존재하는 것과 같은 이치여서 이중 원하는 피라미드의 바닥에 존재하는 벽돌을 찾을 수 있다. 과연 어느 방법이 원하는 벽돌을 찾는데 빠르겠는가? 

    당연히 높이가 낮은 100개의 벽돌로 구성된 피라미드가 더 빠른 시간에 원하는 벽돌을 찾을 수 있을 것이다. 물론, 100개의 벽돌로 만든 10개의 피라미드 중 해당 벽돌을 가지고 있는 하나의 피라미드를 찾는 것은 데이터베이스의 옵티마이저가 바로 찾아주므로 해당 부분은 우리가 걱정할 필요는 없을 것이다. 

    결국, 대용량 테이블에서 인덱스를 이용하여 한 건의 데이터를 액세스하는 경우 인덱스의 높이가 높은 일반 테이블 보다는 높이가 낮은 파티션 테이블과 파티션 인덱스를 이용하는 것이 더 유리하다. 

    앞에서 확인한 것과 같이 배치 작업의 처리와 한 건의 데이터를 추출하는 작업에서 파티션 테이블은 일반 테이블보다 높은 성능을 보장할 수 있다. 이것만으로도 파티션 테이블을 사용해야 하는 이유는 충분하다. 이제 주저하지 말고 대용량 테이블에 대해 파티션 테이블을 적용하도록 하자. 

    권순용 kwontra@hanmail.net|SKC&C에서 DBA 업무를 담당하고 있다. 수 많은 프로젝트에서 DB 아키텍처, 모델링 및 튜닝을 수행했다. 저서로는 정보 문화사의 Perfect! Oracle 실전 튜닝과 초보자를 위한 오라클 10g가 있다. 현재 고급 튜닝 서적을 집필 중이며 성능 최적화와 관련된 특허 출원이 올해의 목표이다.

    Posted by redkite
    , |

    1. CTAS시 가져오지 않는 항목들
    ---------------------
       - DEFAULT
       - CONSTRAINT(PK, FK, CHECK)
       - INDEX
       - Grant

       - Synonym

       - TRIGGER

         *. column name, type, length, not null은 가져옴.

     

    2. 8.1.6 미만 버전
    ---------------------
       - 대량의 테이블을 SORT해서 넣을 경우 (group by를 이용)

         SQL> CREATE TABLE 복사테이블
                           UNRECOVERABLE
                           PARALLEL
                           TABLESPACE 테이블스페이스
              AS
              SELECT 컬럼1, 컬럼2, MIN(컬럼3), ...MIN(컬럼n)
                FROM 테이블
               GROUP BY 컬럼1, 컬럼2; 
    <= 컬럼1, 컬럼2는 PK임.

     

    3. 8.1.6 이상 버전
    ---------------------
       - 대량의 테이블을 SORT해서 넣을 경우 (order by를 지원)
         SQL> ALTER SESSION ENABLE PARALLEL DDL;
         SQL> ALTER SESSION ENABLE PARALLEL DML;
         SQL> ALTER SESSION SET HASH_AREA_SIZE=838860800; -- SORT_AREA_SIZE * 2    
         SQL> ALTER SESSION SET SORT_AREA_SIZE=419430400;    
         SQL> ALTER SESSION SET SORT_AREA_RETAINED_SIZE=419430400;    
         SQL> ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=256;

         SQL> CREATE TABLE 복사테이블
                           [ UNRECOVERABLE ]
                           PARALLEL NOLOGGING
                           TABLESPACE 테이블스페이스
              AS
              SELECT /*+ PARALLEL(A,16) */ * 
                FROM 테이블 A
               ORDER BY ... ;

     

    4. [UN]RECOVERABLE 제한사항
       - 파티션이나 LOB 테이블은 사용 불가
       - UNRECOVERABLE은 subquery 함께 사용할 때만 가능

     

    Posted by redkite
    , |

    Operation

    인덱스

    UNUSABLE 상태 변경

    ADD LOCAL 새로 생성되므로 영향없음
    GLOBAL 파티션만 ADD되므로 영향없음
    DROP LOCAL 함께 DROP되므로 영향없음
    GLOBAL 모든 GLOBAL INDEX가 UNUSABLE
    SPLIT LOCAL SPLIT된 파티션 인덱스 UNUSABLE
    GLOBAL 모든 GLOBAL INDEX가 UNUSABLE
    MERGE LOCAL MERGE되어 남는 파티션 인덱스 UNUSABLE
    GLOBAL 모든 GLOBAL INDEX가 UNUSABLE
    RENAME LOCAL 실제 변경이 없으므로 영향없음
    GLOBAL 실제 변경이 없으므로 영향없음
    MOVE LOCAL MOVE된 파티션 인덱스 UNUSABLE
    GLOBAL 모든 GLOBAL INDEX가 UNUSABLE
    TRUNCATE LOCAL 남은 ROW가 없으므로 영향없음
    GLOBAL 모든 GLOBAL INDEX가 UNUSABLE
    EXCHANGE LOCAL EXCHANGE 한 파티션 인덱스 UNUSABLE
    GLOBAL 모든 GLOBAL INDEX가 UNUSABLE

    ※ ROWID가 변경되는 Operation의 경우 인덱스가 UNUSABLE되므로 Index Rebuild가 필요

    Posted by redkite
    , |

    대용량의 파티션된 로컬 인덱스를 한꺼번에 생성할 때 부하도 크고 시간도 오래 걸린다.
    다음과 같이 UNUSABLE 옵션으로 생성한 다음 각 로컬 인덱스들을 REBUILD하면
    부하도 적고 시간도 절약된다.

     

    1. (필요시) TEMP 테이블스페이스의 크기 늘려줌.
       - 메모리에서 정렬이 모두 이루어지지 않는 경우 TEMP 테이블스페이스의 크기도 늘려줘야 함
       - 임시 테이블스페이스를 별도의 큰 tempfile(datafile이 아닌)로 구성(시간 단축)
       - 오라클 사용자의 TEMPORARY TABLESPACE IMSI로 변경

     

    2. (필요시) PGA 영역의 크기를 늘려줌.
       - init(spfile)의 Parameter 조정
       - HASH_AREA_SIZE의 1/2을 SORT_AREA_SIZE로 사용 가능

     

    3. UNUSABLE된 인덱스를 ACCESS하지 않도록 설정(9i 이상)
       SQL> ALTER SESSION SET SKIP_UNUSABLE_INDEXES = TRUE;

     

    4. PARALLEL이 가능하도록, SORT영역을 크게, 한번에 읽는 블록의 갯수가 많도록 설정
       SQL> ALTER SESSION ENABLE PARALLEL DDL;
       SQL> ALTER SESSION ENABLE PARALLEL DML;

       SQL> ALTER SESSION SET WORKAREA_SIZE_POLICY=MANUAL;
       SQL> ALTER SESSION SET SORT_AREA_SIZE=512000000;
       SQL> ALTER SESSION SET SORT_AREA_RETAINED_SIZE=512000000;
       SQL> ALTER SESSION SET DB_FILE_MULTIBLOCK_READ_COUNT=256;

     

    5. INDEX 생성(UNUSABLE 옵션 적용)

       SQL> CREATE INDEX IX_TBABC10_L1 ON TBABC10(YM, PROC_CD, PROC_STAT) 
                   LOCAL (
                          PARTITION PT_TBABC10_200711 ... ,
                          PARTITION PT_TBABC10_200712 ... ,
                          PARTITION PT_TBABC10_200801 ... ,
                          PARTITION PT_TBABC10_200802 ... ,
                          PARTITION PT_TBABC10_200803 ... ,
                          PARTITION PT_TBABC10_999999 ...  
                        )
                    UNUSABLE ;

     

    6. 각 파티션별로 REBUILD
       가장 많이 ACCESS(최근 사용)되는 파티션부터 생성
       SQL> ALTER INDEX IX_TBABC10_L1 REBUILD PARTITION PT_TBABC10_999999
                          TABLESPACE TS_TBABC10_999999 PARALLEL(DEGREE 8) NOLOGGING;

       SQL> ALTER INDEX IX_TBABC10_L1 REBUILD PARTITION PT_TBABC10_200803 
                          TABLESPACE TS_TBABC10_200803 PARALLEL(DEGREE 8) NOLOGGING;
                    :
       SQL> ALTER INDEX IX_TBABC10_L1 NOPARALLEL LOGGING;

     

    특히, PK(PRIMARY KEY)를 생성하는 경우 ALTER TABLE ... ADD CONSTRAINTS ... 명령을

    이용하면 위와같이 작업할 수 없다. 그러므로 다음과 같은 순서로 작업하면 된다.
    1) CREATE UNIQUE INDEX ... UNUSABLE;
    2) ALTER INDEX ... REBUILD ... ;
    3) ALTER INDEX ... NOPARALLEL LOGGING;
    4) ALTER TABLE ... ADD CONSTRAINTS ... PRIMARY KEY (...) ;

     

    Posted by redkite
    , |

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

    1. DELTE

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

       - DML(Data Manuplation Language)
       - 사용 : DELETE FROM 테이블명 WHERE 조건;

       - 데이터의 일부 또는 전부를 삭제 할 수 있음
       - 삭제할 데이터들은 디스크에서 메모리로 로딩(loading)한 후 작업
       - Disk I/O가 많이 발생하므로 메모리(Data buffer cache)에 있는 데이터를 삭제하기 전에
         삭제할 데이터에 해당하는 Redo data, Undo Data를 생성
       - COMMIT하기 전 ROLLBACK이 가능
       - 삭제할 데이터를 해당 블록 내에서 하나씩 찾아서 삭제하기 때문에 시간이 오래 걸림
       - 모두 삭제하였을 때도 사용된 블럭의 EXTENT 및 HWM을 그대로 유지   
       - 삭제(delete)하는 테이블의 데이터를 참조(reference)하는 테이블의 데이터에서
         row단위로 체크해서 참조하는 테이블에 row가 없으면 삭제 가능
       - 삭제한 row의 수를 반환함
       - delete로 삭제된 데이터는 FLASHBACK QUERY로 복구 가능

       - 통계정보(Analyze)를 변경하지 않음

     

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

    2. TRUNCATE
    -------------------------------------

       - DDL(Data Definition Language)
       - 사용 : TRUNCATE TABLE 테이블명;

       - 데이터의 일부 삭제는 불가, 전부 삭제됨
       - DDL(Data Definition Language) 이므로 데이터를 메모리로 로딩(Loading)하지 않음
       - Undo, Redo log를 생성하지 않음

         (테이블이나 인덱스가 차지하고 있던 저장공간 관리영역에 대한 적은 량의 redo log는 생성함)
       - Undo, Redo log를 생성하지 않기 때문에 ROLLBACK 불가, 복구 불가
       - 타 테이블에서 참조하고 있으면 Truncate 불가
       - 모두 삭제되기 때문에 사용된 EXTENT 및 HWM을 초기화
       - 참조(reference) 제약이 ENABLE인 경우 ORA-02266 오류 발생
       - 실제 해당 데이터를 일일이 찾아서 지우지 않고,

          데이터 딕셔너리(Data Dictionary)에 삭제 표시 및
          빈 공간 표시만을 하기 때문에 빠른 시간 내에 시스템 부하없이 데이터를 삭제 가능
       - 삭제한 row의 수를 반환하지 않음
       - Truncate한 Table 하나만을 특정 시점으로 복구 시 Archive Log Mode로 운영 중인 경우에

          Hot Backup 또는 Cold Backup을 이용한 별도 데이터베이스에서 Incomplete Recovery를

          한 후 해당 테이블을 exp/imp해야 복구 가능
       - truncate table로 삭제된 데이터는 FLASHBACK QUERY로도 복구 불가

         (ORA-01466: unable to read data - table definition has changed)

       - 통계정보(Analyze)를 변경함

     

    Posted by redkite
    , |

    V$SESSION(GV$SESSION) COMMAND 컬럼의 코드표.

     

    Table 7-5 COMMAND Column of V$SESSION and Corresponding Commands

    Number Command Number Command
    1 CREATE TABLE 2 INSERT
    3 SELECT 4 CREATE CLUSTER
    5 ALTER CLUSTER 6 UPDATE
    7 DELETE 8 DROP CLUSTER
    9 CREATE INDEX 10 DROP INDEX
    11 ALTER INDEX 12 DROP TABLE
    13 CREATE SEQUENCE 14 ALTER SEQUENCE
    15 ALTER TABLE 16 DROP SEQUENCE
    17 GRANT OBJECT 18 REVOKE OBJECT
    19 CREATE SYNONYM 20 DROP SYNONYM
    21 CREATE VIEW 22 DROP VIEW
    23 VALIDATE INDEX 24 CREATE PROCEDURE
    25 ALTER PROCEDURE 26 LOCK
    27 NO-OP 28 RENAME
    29 COMMENT 30 AUDIT OBJECT
    31 NOAUDIT OBJECT 32 CREATE DATABASE LINK
    33 DROP DATABASE LINK 34 CREATE DATABASE
    35 ALTER DATABASE 36 CREATE ROLLBACK SEG
    37 ALTER ROLLBACK SEG 38 DROP ROLLBACK SEG
    39 CREATE TABLESPACE 40 ALTER TABLESPACE
    41 DROP TABLESPACE 42 ALTER SESSION
    43 ALTER USER 44 COMMIT
    45 ROLLBACK 46 SAVEPOINT
    47 PL/SQL EXECUTE 48 SET TRANSACTION
    49 ALTER SYSTEM 50 EXPLAIN
    51 CREATE USER 52 CREATE ROLE
    53 DROP USER 54 DROP ROLE
    55 SET ROLE 56 CREATE SCHEMA
    57 CREATE CONTROL FILE 59 CREATE TRIGGER
    60 ALTER TRIGGER 61 DROP TRIGGER
    62 ANALYZE TABLE 63 ANALYZE INDEX
    64 ANALYZE CLUSTER 65 CREATE PROFILE
    66 DROP PROFILE 67 ALTER PROFILE
    68 DROP PROCEDURE 70 ALTER RESOURCE COST
    71 CREATE MATERIALIZED VIEW LOG 72 ALTER MATERIALIZED VIEW LOG
    73 DROP MATERIALIZED VIEW LOG 74 CREATE MATERIALIZED VIEW
    75 ALTER MATERIALIZED VIEW 76 DROP MATERIALIZED VIEW
    77 CREATE TYPE 78 DROP TYPE
    79 ALTER ROLE 80 ALTER TYPE
    81 CREATE TYPE BODY 82 ALTER TYPE BODY
    83 DROP TYPE BODY 84 DROP LIBRARY
    85 TRUNCATE TABLE 86 TRUNCATE CLUSTER
    91 CREATE FUNCTION 92 ALTER FUNCTION
    93 DROP FUNCTION 94 CREATE PACKAGE
    95 ALTER PACKAGE 96 DROP PACKAGE
    97 CREATE PACKAGE BODY 98 ALTER PACKAGE BODY
    99 DROP PACKAGE BODY 100 LOGON
    101 LOGOFF 102 LOGOFF BY CLEANUP
    103 SESSION REC 104 SYSTEM AUDIT
    105 SYSTEM NOAUDIT 106 AUDIT DEFAULT
    107 NOAUDIT DEFAULT 108 SYSTEM GRANT
    109 SYSTEM REVOKE 110 CREATE PUBLIC SYNONYM
    111 DROP PUBLIC SYNONYM 112 CREATE PUBLIC DATABASE LINK
    113 DROP PUBLIC DATABASE LINK 114 GRANT ROLE
    115 REVOKE ROLE 116 EXECUTE PROCEDURE
    117 USER COMMENT 118 ENABLE TRIGGER
    119 DISABLE TRIGGER 120 ENABLE ALL TRIGGERS
    121 DISABLE ALL TRIGGERS 122 NETWORK ERROR
    123 EXECUTE TYPE 157 CREATE DIRECTORY
    158 DROP DIRECTORY 159 CREATE LIBRARY
    160 CREATE JAVA 161 ALTER JAVA
    162 DROP JAVA 163 CREATE OPERATOR
    164 CREATE INDEXTYPE 165 DROP INDEXTYPE
    167 DROP OPERATOR 168 ASSOCIATE STATISTICS
    169 DISASSOCIATE STATISTICS 170 CALL METHOD
    171 CREATE SUMMARY 172 ALTER SUMMARY
    173 DROP SUMMARY 174 CREATE DIMENSION
    175 ALTER DIMENSION 176 DROP DIMENSION
    177 CREATE CONTEXT 178 DROP CONTEXT
    179 ALTER OUTLINE 180 CREATE OUTLINE
    181 DROP OUTLINE 182 UPDATE INDEXES
    183 ALTER OPERATOR

     

    Posted by redkite
    , |

    No.

    서버

    작업순서

    작업내용

    비고

    1

    OLD

    테이블스페이스 파악

    각 업무별 Tablespace Size 조회

     

    2

    OLD

    User/Role 정보 및 권한 파악

    User Default Tablespace Temporary, Password, Lock&Expire, Profile

     

    3

    OLD

    User Object수 파악

    이관 작업 후 확인자료로 사용하기 위한 Object 수 파악(Table/Index/PK/FK/CK/SP/SF/Trigger/Sequece/Grant 등등)

     

    4

    OLD

    Object Status

    이관 작업 후 확인자료로 사용하기 위한 점검 리스트 작성
    (Role/Table/Index/PK/FK/CK/SP/Trigger/Sequece/Synonym/Grant
    등등)

     

    5

    OLD

    Listener Stop

    lsnrctl stop 리스너명

     

    6

    OLD

    DB Shutdown

    SQL> shutdown immediate

     

    7

    OLD

    DB Startup

    SQL> startup

     

    8

    OLD

    User Export

    각 업무 단위(User Mode) Export(Rows=n Indexes=n Option 사용)

    크기가 크지 않은 경우 Rows=y Indexes=y로 하고 [9] 생략

    9

    OLD

    테이블별 Export

    테이블의 크기가 큰 것은 별도 Export
    테이블의 크기가 작은 것들은 모아서 Export하되 적당한 크기로 분할하여 Export

    Direct=y Buffer=102400000 사용

    10

    OLD

    Export .dmp 파일 신규 서버로 복사

    Ftp 또는 rcp, NFS 등을 이용하여 복사
    용량이 부족할 경우 나누어 Export한 파일을 순차적으로 복사하여 Import한 다음 삭제하는 방법으로 작업

    NFS를 이용하여 직접 Import는 권장하지 않음

    1

    NEW

    DB Create

    DB 재생성 작업

     

    2

    NEW

    Tablespace 재생성

    업무별, 부하분산 고려한 Tablespace 생성

     

    3

    NEW

    User Role 생성

    User/Role생성 후 권한 부여

     

    4

    NEW

    User Import

    UserExport(rows=n indexes=n) .dmp 파일 Import

    필요시 테이블/인덱스 initial size 조정 

    5

    NEW

    Foregin Key Disable

     

     

    6

    NEW

    Trigger Disable

     

     

    7

    NEW

    테이블별 Import

    테이블별 Export .dmp 파일들을 동시에 Import를 여러 개 실행(Ignore=y Commit=y Buffer=102400000)

    OLD [9]을 수행하지 않은 경우 이 작업은 Skip

    8

    NEW

    Foregin Key Enable

     

     

    9

    NEW

    Trigger Enable

     

     

    10

    NEW

    User Object수 파악

    이관 작업 후 확인자료로 사용하기 위한 Object 수 파악(Role/Table/Index/PK/FK/CK/SP/SF/Trigger/Sequece/Grant 등등)

     

    11

    NEW

    Object Status

    이관 작업 후 확인자료로 사용하기 위한 점검 리스트 작성
    (Role/Table/Index/PK/FK/CK/SP/Trigger/Sequece/Synonym/Grant
    등등)

     

    12

    NEW

    Object 확인 작업

    DB 이관 전/후 비교

     

    13

    NEW

    Table Analyze 작업

    DBMS_STATS Packge를 이용하여 통계정보 생성

     

    14

    NEW

    DB Shutdown

    SQL> shutdown immediate

     

    15

    NEW

    DB Startup

    SQL> Startup

     

    16

    NEW

    Listener Start

    lsnrctl start 리스너명

     

    17

    NEW

    응용 프로그램 테스트

    응용프로그램을 실행하여 테스트 실시

     

    18

    NEW

    Listener Stop

    lsnrctl stop 리스너명

     

    19

    NEW

    DB Shutdown

    SQL> shutdown immediate

     

    20

    NEW

    Archive log Mode 적용

    init$SID.ora 수정
    SQL> Startup mount
    SQL> alter database archivelog
    SQL> archive log list
    SQL> alter database open
    SQL> shutdown immediate

     

    21

    NEW

    DB Clod백업

    백업 장비(BCV,DLT,DAT )을 이용한 데이터파일 백업

    Posted by redkite
    , |
    DECODE()함수의 Return 값
     
    Return값 = DECODE(expr, search, result [, search, result ]...  [, default ])
    <pre> </pre>
    - DECODE() 함수의 Return값첫번째 result(3번째 인수)의 Datatype에 따라 달라진다.
    - 첫번째 result(3번째 인수)가 null 이라면 Return값의 Datatype은 String이다.
    - 첫번째 result(3번째 인수)와 나머지 result나 defaut의 Datatype이 다르면
      "ORA-01722: 수치가 부적합합니다." 에러가 발생한다.

     

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

    Oracle® Database SQL Reference
    10g Release 2 (10.2)
    Part Number B14200-02

     

    [Purpose]

    DECODE compares expr to each search value one by one.

    If expr is equal to a search, then Oracle Database returns the corresponding result.

    If no match is found, then Oracle returns default.

    If default is omitted, then Oracle returns null.

     

    The arguments can be any of the numeric types (NUMBER, BINARY_FLOAT, or BINARY_DOUBLE)

    or character types.

    - If expr and search are character data,

    then Oracle compares them using nonpadded comparison semantics.

    expr, search, and result can be any of the datatypes CHAR, VARCHAR2, NCHAR, or NVARCHAR2.

    The string returned is of VARCHAR2 datatype and is in the same character set

    as the first result parameter.

    - If the first search-result pair are numeric,

    then Oracle compares all search-result expressions and the first expr to determine

    the argument with the highest numeric precedence, implicitly converts the remaining

    arguments to that datatype, and returns that datatype.

     

    The search, result, and default values can be derived from expressions.

    Oracle Database uses short-circuit evaluation.

    That is, the database evaluates each search value only before comparing it to expr,

    rather than evaluating all search values before comparing any of them with expr.

    Consequently, Oracle never evaluates a search if a previous search is equal to expr.

    Oracle automatically converts expr and each search value to the datatype

    of the first search value before comparing.

     

    Oracle automatically converts the return value to the same datatype as the first result.

    If the first result has the datatype CHAR or if the first result is null,

    then Oracle converts the return value to the datatype VARCHAR2.

     

    In a DECODE function, Oracle considers two nulls to be equivalent.

    If expr is null, then Oracle returns the result of the first search that is also null.

     

    The maximum number of components in the DECODE function, including expr,

    searches, results, and default, is 255.

    Posted by redkite
    , |
    • Primary Key 컬럼 순서와 데이터베이스 성능

    프린트 이메일 전송

    데이터 처리를 할 때 Primary Key(PK)의 역할은 거의 절대적이라 할 수 있다. 웬만한 트랜잭션 처리는 모두 PK를 이용해 처리하기 때문이다. 만일 우리가 설계하는 데이터 모델에서 PK를 이용하는 부분에 비효율적인 면이 있다면 어떻게 될까? 그것이 주는 영향은 단순히 SQL 몇 개를 튜닝하는 정도가 아니라, 해당 테이블을 이용하는 전 트랜잭션에 영향을 주는 치명적인 문제가 될 수 있다.

    이춘식 csklee11@chol.com|현재 LG CNS DB관리팀장으로 국내외 데이터베이스를 총괄해 운영하는 리더를 맡고 있다. '데이터베이스 설계와 구축'이라는 전문서로 수많은 프로젝트 설계방법을 제시했으며, 진단을 통한 실질적인 문제점과 개선점을 제시했다. 데이터베이스 영역과 함께 기술사/감리사로서 강의, 집필, 평가위원, 자격 검정위원 등의 활동을 수행하고 있다. 저서로 데이터베이스 설계와 구축, 아는 만큼 보이는 데이터베이스 등 총 9권이 있다.

    IT 프로젝트를 진행하면서 테이블을 추가하거나 변경할 때, PK 컬럼 구성과 컬럼 순서를 중요하게 생각하지 않은 채 있는 그대로 테이블을 생성하는 경우가 많이 있다.

    실제로 많은 프로젝트를 진단해보면, 설계단계 끝 부분을 트랜잭션이 들어오는 특성을 분석해 적절하게 PK 순서를 조정해야 되지만 그렇지 않고 데이터 모델링이 된 그대로 DDL(Data Definition Language)을 생성해 테이블을 만들어버리는 경우가 비일비재하다. 그렇게 한참을 개발 한 후, 충분한 테스트 데이터를 생성해 실행하면 성능이 좋지 않아 그때 새로운 인덱스를 추가해 결국 오버헤드를 유발하는 경우가 발생한다.

    테이블 생성 이전에 SQL Where 절 조건을 분석하라
    여러 개의 컬럼으로 구성된 PK 구성 테이블을 있는 그대로 생성할 경우 어떤 일이 발생할까? 먼저 의도하지 않은 인덱스 구성인 'Primary Key Unique Index'가 생성될 것이다. 그에 따라 조회 SQL 실행 시 성능이 저하되거나 설계로 인해 생성된 인덱스 외의 추가 인덱스를 생성하게 돼 결국 인덱스 과다로 입력, 수정, 삭제 등 불필요한 내부 작업이 증가 할 것이다. 결국 불필요한 작업으로 또 다른 성능저하의 원인이 된다.

    따라서 테이블 생성 이전에 설계단계에서 엔티티 타입의 PK 컬럼 순서를 SQL Where 절을 분석해 조정하는 작업이 위 문제를 해결하는데 가장 중요한 열쇠가 된다.

    데이터베이스는 분석 - 설계 - 구축 - 테스트 - 이행의 IT 프로젝트 진행 라이프 사이클 중 설계단계 이후에 완성된 데이터 모델을 토대로 물리적인 테이블을 정해진 DBMS에 맞게 생성한다. 보통 상용화된 데이터모델링 툴(ERStudio, ERWin, DA# 등)을 이용해 데이터 모델을 만들고 DDL을 생성하기 때문에, 설계단계 말 즉 데이터베이스 생성단계에서 PK 컬럼의 순서를 고려하지 않고 생성하는 경우가 많이 있다.

    <그림 1> PK 순서, 반드시 조정해라

    PK 순서를 잘못 지정해 성능이 저하된 경우(단순한 오류)
    설계단계에서 데이터모델링을 종료하고 스키마 정보를 생성하기 전, 데이터 모델의 PK 순서를 조정하지 않고 테이블을 생성하면 인덱스를 이용하지 못해 테이블을 Full Scan하는 현상이 발생될 수 있다.

    학사관리 업무에서 데이터 모델에 따른 PK 구성을 예로 들어보겠다. '입시마스터'라는 테이블의 PK는 수험번호+년도+학기로 구성돼 있고, '전형과목 실적' 테이블은 입시마스터 테이블에서 상속받은 수험번호+년도+학기에 전형과목 코드로 PK가 구성돼 있는 복합 식별자 구조의 테이블이다.

    입시마스터에는 200만 건의 데이터가 있으며 학사는 4학기로 구성돼 있다. 데이터의 보관은 5년이다. 한 학기당 평균 1만 건의 데이터가 있다고 가정해보겠다. PK가 수험번호+년도+학기로 구성돼 있으므로 대부분의 자동 DDL 생성 툴은 세 개의 컬럼을 순서 그대로 조합한 인덱스를 생성한다. 이때 입시 마스터의 인덱스를 '입시마스터_I01'이라 하자.

    이 테이블 구조에서 <그림 2>와 같은 SQL구문이 실행되면, 입시마스터 테이블에 있는 인덱스 입시마스터_I01을 이용할 수 있을까?

    <그림 2> PK 순서에 따라 생성된 인덱스와 SQL - 개선 전

    입시마스터_I01 인덱스가 수험번호+년도+학기 중 수험번호에 대한 값이 WHERE 절에 들어오지 않으므로, FULL TABLE SCAN이 발생해 200만 건의 데이터를 모두 읽어 당연히 성능이 저하됐다. 다수의 IT 프로젝트 현장 DB 담당자가 PK컬럼 순서를 조정하지 않아, 인덱스를 이용해야 하는 SQL 문장이 결국 인덱스를 이용하지 못해 성능저하가 발생하는 것이다.

    이런 테이블 구성을 효율적으로 개선하기 위해서 인덱스의 정렬구조를 고려하고, 해당 테이블에 들어오는 조회조건을 사전에 분석시켰다. 그래서 설계된 화면정의서의 조회조건을 분석하고, 설계된 SQL 문장의 Where절에 '='이나 'Between', '<', '>' 등을 분석해 PK 컬럼 순서를 범위가 들어오는 조건부터 앞쪽으로 위치시켰다. 즉, 입시마스터 엔티티 타입의 PK 순서를 SQL Where 절에서 조회하는 조건으로 조정했다.

    <그림 3> PK 순서에 따라 생성된 인덱스와 SQL - 개선 후

    입시마스터 테이블의 데이터를 조회할 때, 년도와 학기에 대한 내용이 빈번하게 들어오므로 이를 이용해 PK순서를 변경, 인덱스를 이용 가능하게 만들었다.

    생성된 인덱스가 정상적으로 이용돼 평균 2만 건의 데이터를 처리할 수 있게 돼 성능이 한층 개선됐다. 단순하게 PK 순서만 조정했을 뿐인데 SQL 문장 실행에서 Full Table Scan을 했는지 아니면 Index Scan을 했는지 결정된 것이다. 좀 더 복잡한 다른 예를 보자.

    PK 순서를 잘못 지정해 성능이 저하된 경우(복잡한 오류)
    A씨는 은행권의 프로젝트를 진행하면서 현금 출급기의 PK 컬럼에 대해 유일성과 최소성 확보를 데이터모델링에선 고려했으나 PK 컬럼 순서에는 신경쓰지 않고 바로 테이블을 생성했다.

    이렇게 생성된 현금출급기 실적 테이블의 PK는 거래일자+사무소코드+출급기 번호+명세표번호로 구성돼 있는데, 대부분의 SQL 문장에서는 조회를 할 때 사무소 코드가 '='로 들어오고 거래일자는 'BETWEEN' 조회를 하고 있다. 과연 이러한 유형의 트랜잭션 성능이 좋은 상태로 나타나는 것일까?

    <그림 4> PK 순서에 따라 생성된 인덱스와 SQL - 개선 전

    A씨가 PK 컬럼을 조정하지 않고 해당 테이블에 업무적인 규칙에 의해 작성된 SQL 구문 이다. 자 그렇다면 인덱스를 이용해볼까? 과연 인덱스를 사용하는 것이 최선의 성능을 가져오는 것일까?

    실행계획을 분석하면 인덱스가 정상적으로 이용됐기 때문에 SQL 문장은 잘 튜닝된 것으로 착각할 수 있다. 문제는 인덱스를 이용은 하는데 넓은 Range 조회로 인해 SQL 실행성능이 심각하게 저하된다는 점이다.

    <그림 5>는 인덱스를 '거래일자+사무소 코드' 순서로 구성 할 때와 '사무소 코드+거래일자'로 구성할 때 각각 데이터를 처리하는 범위가 어떻게 달라지는지 보여주고 있다.

    <그림 5> 인덱스 구성에 따른 데이터 처리범위 변경

    거래일자+사무소코드로 구성된 왼쪽은 BETWEEN 비교를 한 거래일자 '20120701'이 인덱스의 앞에 위치하기 때문에 범위가 넓어졌고, 사무소 코드+거래일자로 구성된 오른쪽은 인덱스의 경우 = 비교를 한 사무소 코드 '000368'가 인덱스 앞에 위치해 범위가 좁아졌다. 따라서 인덱스 순서를 고려해 데이터 모델의 PK 순서를 거래일자+사무소 코드+출급기 번호+명세표 번호에서 사무소 코드+거래일자+출급기 번호+명세표 번호로 수정하면 성능을 개선시킬 수 있다.

    <그림 5> 구조를 토대로 <그림 6>과 같이 테이블의 PK 구성을 다시 조정해 PK 인덱스 조회 시 범위를 줄여 성능향상을 유도했다.

    <그림 6> PK 순서에 따라 생성된 인덱스와 SQL - 개선 후

    물론 테이블의 PK 구조를 그대로 둔 상태에서 인덱스만 하나 더 만들어도 성능은 개선될 수 있다. 하지만 이미 만들어진 PK 인덱스가 전혀 사용되지 않는다면 입력, 수정, 삭제 시 불필요한 인덱스로 인한 성능저하를 피할 수 없게 된다. 결국 최적화된 인덱스 생성을 위해는 PK 순서변경을 통한 인덱스 생성이 바람직하다. 테이블을 구축하기 이전에 PK 구성단계에서부터 최적의 성능을 나타낼 수 있도록 PK 순서를 조정하는 작업을 반드시 수행해야 한다.

    PK 컬럼 순서구성에 대한 결론
    지금까지 강조해 온 PK 컬럼 순서를 통한 성능저하 예방의 핵심은 프로젝트 설계단계의 마지막인 데이터모델링 수행 시 다수의 컬럼을 사용하는 PK 컬럼 순서에 대해 트랜잭션의 패턴을 분석하고, 이를 검토해 조정해야 한다는 것이다.

    PK 순서가 잘못돼 SQL 문장의 성능이 저하되는 경우는 크게 두 가지다. 첫 번째는 단순한 오류 사례와 같이 인덱스를 이용하지 못하고 FULL TABLE SCAN 하면서 성능이 저하되는 경우이다. 두 번째는 복잡한 오류 사례와 같이 인덱스는 이용하는데 그 범위가 넓어져 성능이 저하되는 경우다.

    보통 첫 번째는 SQL의 실행계획을 보고 쉽게 튜닝을 하는데, 두 번째의 경우는 UNIQUE 인덱스를 이용해야 하지만 성능이 저하되는 이유로 인해 쉽게 튜닝을 못 하는 경우가 빈번하다. 따라서 인덱스의 정렬(SORT) 구조를 이해한 상태에서 트랜잭션의 특성에 따른 PK 구성을 해 인덱스 범위를 최소화 하는 방향을 데이터 모델에 반영해야 한다.

    데이터 모델의 PK 순서가 아무 의미 없는 것 같지만 실전 프로젝트에서 의미 없는 PK 순서를 설계해 성능이 저하되는 경우가 아주 빈번하다. 성능저하 현상의 대부분이 PK가 여러 개의 속성으로 구성된 복합 식별자일 때 PK 순서를 고려하지 않고 데이터모델링을 한 경우에서 온다. 이건 단순하지만 자주 범하는 오류다.

    이렇게 발생한 설계상의 오류를 해결하기 위해 SQL 구문을 수정하거나 인덱스를 생성하는 방법을 사용하려고 한다. 하지만 이것은 구불구불한 길을 만들어 놓고 그 길을 빨리 가기 위해 또 다른 지름길을 만들거나 구불구불한 길을 빨리 갈 수 있는 차를 임시로 개발하는 것과 같다. 결코 근본적인 해결법이 아니라는 말이다.

    특히 물리적인 데이터모델링 단계에서는 스스로 생성된 PK 순서 이외에 다른 엔티티 타입으로부터 상속받아 발생되는 PK 순서까지 항상 주의해 표시해야 한다. PK는 해당 테이블의 데이터에 가장 빈번하게 접근해 사용되는 유일한 인덱스(Unique Index)를 모두 자동으로 생성한다.

    PK 순서를 결정하는 기준은 인덱스 정렬구조를 이해한 상태에서 인덱스를 효율적으로 이용할 수 있도록 그 순서를 지정하는 것이다. 즉 인덱스는 그 특성 상 여러 개의 속성이 인덱스 하나로 구성돼 있을 때 앞쪽에 위치한 속성값이 비교자로 있어야 좋은 효율을 나타낸다. 앞쪽에 위치한 속성값이 가급적 '=' 이나 최소한 범위인 'BETWEEN', '< >'에 들어와야 이용할 수 있다. 다시 말하면 그 특성 상 어떤 값이 들어오는지를 예상하고, 데이터베이스에서 발생하는 트랜잭션의 성격을 제대로 이해하지 못하면 원활한 PK 순서를 지정할 수 없게 된다는 것이다. 즉, 데이터모델링에 참여한 사람이 정확한 프로세스의 특징을 이해하지 못한다면 PK 순서를 정확하게 지정할 수 없다는 의미이다.

    필자 메모

    PK 순서에 대해 자주하는 질문들
    Q PK 순서를 고려하지 않아도, 설계 때와는 다른 순서로 인덱스를 만들면 되지 않을까요?

    A 대부분의 프로젝트 진단결과, 엔터티 타입의 PK 순서를 그대로 인덱스로 생성하지 별도의 순서로 인덱스를 만드는 경우는 한 군데도 없다. 결국 모든 경우에서 설계된 순서 그대로 인덱스를 생성하는 것이다. 테이블을 생성하는 DBA가 똑똑해서 일부러 설계된 순서와 다르게 PK 인덱스를 생성하면 문제되지 않는다. 하지만 이때도 관리되는 데이터 모델과 데이터베이스의 불일치로 구성관리의 문제라는 또 다른 이슈가 대두될 것이다.

    Q 만약 컬럼 순서가 문제된다면 또 하나의 인덱스를 만들면 어떤가요? 예를 들어 A+B 컬럼 순서로 만들었는데 효율이 떨어진다면 B+A로 한 개 더?

    A 불필요한 인덱스 한 개는 입력, 수정, 삭제의 효율성을 떨어뜨리는 요인이 된다. 개발은 한 번이지만 시스템이 실제 업무를 지원하기 위해 서비스 되는 기간은 수 십년 동안 지속된다는 점을 기억해야 한다. 불필요한 인덱스로 인해 데이터 처리(입력, 수정, 삭제)의 비용이 지속적으로 많이 소요되는 구조가 될 것이다.

    Q 만약 컬럼 순서가 문제가 된다면 기존의 생성된 인덱스는 Drop하고 새롭게 만들면 어떤가요? 예를 들어 A+B 컬럼 순서로 만들었는데 효율이 떨어지면 이것을 Drop하고 B+A로 새로 만들면?

    A 실제 프로젝트에서 튜닝은 대부분 실제 데이터를 이관하는 구축 중·후반에 수행된다. 이때 인덱스를 Drop하면 얼마나 많은 SQL 문이 영향을 받을지 장담할 수 없다. 이 말은 Drop을 쉽게 의사결정하지 못한다는 의미다. 더군다나 PK는 테이블 자신 안에서만 사용되는 것이 아니라 다른 테이블로 계속 상속되는 특징을 가지고 있기 때문에 Drop 하려면 연관된 모든 테이블을 고려해야 한다.

    따라서 설계단계에서 PK 순서를 조정하고 이후 최적화 작업을 하면서 일부를 조정하는 것이 가장 베스트 솔루션이라 할 수 있다.

    Posted by redkite
    , |
    오라클의 공유 메모리는 커널 작업을 수반하지 않으며 프로세스 간의 데이터 복제 작업이 불필요 하기 때문에 , IPC(inter-Process Communication)를 위한 가장 빠른 방법을 선호 되고 있습니다.
    오라클은 인스턴스 구조를 보면 SGA(System Global Area)와 백 그라운드 프로세스로 구성 되어 있습니다. 여기에서 SGA는 Shared Pool, Database Buffer Cache, Redo Log Buffer 등의 저장에 활용되므로 SGA 크기에 따라 오라클 성능이 크게 달라집니다.

    그럼 오라클을 설치하기 전에 Linux에서 사용하는 커널 매개변수와 Shell Limit에 대해서 알아 보도록 하겠습니다.


    1. Linux 커널 매개변수의 설정

    다른 UNIX 시스템과 달리, Linux 운영체제에서는 시스템이 실행 중인 상태에서 대부분의 커널 매개변수를 수정할 수 있습니다. 커널 매개변수를 변경한 뒤에 시스템을 리부팅할 필요도 없습니다. Oracle Database 10g가 요구하는 커널 매개변수 설정할 수 있으나 시스템에 이미 커널 메게변수가 높은 수치로 설정 되어있을 경우에는 변경 하지 마시기 바랍니다.

    [ 매개변수 - kernel.shmmax ] SHMMAX 매개변수는 공유 메모리 세그먼트의 최대 크기(바이트 단위)를 정의하는데 사용됩니다. 오라클 SGA 메모리로 구성되며, SHMMAX가 올바르게 설정되지 않을 경우 SGA의 크기가 제약될 수도 있습니다. 따라서 SGA의 크기보다 작지 않도록 SHMMAX를 설정합니다.
    총 10GB의 메모리에서 오라클이 사용할 수 있는 최대 한도를 정의 하는 것을 의미 합니다.
    먄약 SHMMAX 매개변수가 잘못 설정될 경우에는 아래와 같은 오라클 에러 메시지가 발생 됩니다.

    ORA-27123: unable to attach to shared memory segment

    기본적으로 SHMMAX 기본 값은 32MB 입니다. SGA로 사용하기에는 너무 적은 공간이기 때문에 적당한 크기로 변경 해야 합니다.
    [2010-12-09 03:26:18]-[root@calmmass:/proc/sys/kernel]
    # cat /proc/sys/kernel/shmmax |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    33.554.432

    [ 매개변수 - kernel.shmall ]
    SHMALL 커널 매개변수는 특정 시점에 시스템에서 사용 가능한 공유 메모리의 최대 크기(페이지 단위)를 설정하는데 사용됩니다. 따라서 이 매개변수는 최소한 아래 값보다 커야 합니다.
    ceil(SHMAX/PAGE_SIZE)
    우선 페이지 사이즈를 확인 해보겠습니다. Red Hat Linux의 페이지 사이즈는 기본적으로 4,096 바이트 입니다.
    [2010-12-09 02:51:29]-[root@calmmass:~]
    # getconf PAGESIZE |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    4.096

    SHMALL의 디폴트 사이즈는 아래와 같습니다. sed를 이용해서 3자리로 구분한 결과 2MB를 사용합니다.
    # cat /proc/sys/kernel/shmall |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    2.097.152

    [ 매개변수 - kernel.shmmni ] SHMMNI는 공유 메모리 식별자의 개수를 의미 하며 권장 값은 100 이상 으로 설정하셔야 합니다. 아래와 같이 확인 하시기 바랍니다.
    [2010-12-09 03:26:07]-[root@calmmass:/proc/sys/kernel]
    # cat shmmni
    4096

    [ 매개변수 - kernel.sem ] 세마포어(Semaphore)란 공유 리소스의 사용 과정에서 프로세스(프로세스 내 쓰레드) 간의 동기화를 위해 사용되는 일종의 카운터(counter) 입니다. 애플리케이션의 세마포어를 이용하면, 운영 체제는 SET를 통해 세마포어를 지원하게 됩니다.
    그럼 세마포어 설정 값을 먼저 확인 해보겠습니다.
    [2010-12-09 03:39:32]-[root@calmmass:/proc/sys/kernel]
    # ipcs -lm
    ------ Shared Memory Limits --------
    max number of segments = 4096
    max seg size (kbytes) = 32768
    max total shared memory (kbytes) = 8388608
    min seg size (bytes) = 1

    SEM 매개변수는 세마포어 최대값을 조정 가능하며 의미는 아래와 같습니다.
    의미 : {배열당 최대 SEM 수}{최대SEM시스템넓이}{SEMOP호출당최대OP수}{최대배열수}
    [2010-12-09 03:37:24]-[root@calmmass:/proc/sys/kernel]
    # cat /proc/sys/kernel/sem |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    250 32.000 32 128
    1 2 3 4 (필드 라고 정의 하겠습니다.)
    1 필드 값(250) : SEMMSL 매개변수는 세미포어 set 당 세마포어의 최대 개수를 정의합니다. 오라클을 예로 init[SID].ora 파일(pfile, spfile 동일)의 PROCESS 인스턴스 매개변수의 (전체 데이터베이스 중) 최대값에 10을 더한 값을 사용할 것을 권장하고 있습니다. 그래서 100이상의 값을 정의하는 것을 권장 합니다.

    2 필드 값(32.000) : SEMMNI 매개변수는 전체 Linux 시스템의 세마포어 set의 개수를 정의 합니다. 오라클에서는 100이상의 값을 정의하는 것을 권장합니다.

    3 필드 값(32) : SEMMNS 매개변수는 전체 Linux 시스템의 ("세마포어 set"이 아닌) 세마포어의 최대 개수를 정의 합니다. 오라클은 인스턴스 매개변수의 값을 모두 더한 뒤, 가장 큰 Processes값을 두 차례 더하고, 마지막으로 각 데이터베이스 별로 10을 더한 값을 SEMMNS를 설정하도록 권장 합니다.

    4. 필드 값(128) : SEMOPM 매개변수는 semop 시스템 호출(system call) 별로 수행될 수 있는 세마포어 작업의 수를 설정하는데 사용됩니다.SEMOPM을 SEMMSL과 동일하게 설정하는 것을 권장합니다. 오라클에서는 100이상의 값으로 설정할 것을 권장 하고 있습니다.


    [ 매개변수 - fs.file-max ] Red Hat Linux 서버를 구성하는 과정에서, 사용 가능한 파일 핸들(file handle)의 수가 충분한지 확인하는 것을 매우 중요합니다. 파일 핸들의 최대 개수 설정에 따라 Linux 시스템에서 동시에 오픈할 수 있는 파일의 수가 달라지게 됩니다.
    오라클은 전체 시스템의 파일 핸들 수를 최소 65536개 이상으로 설정 할 것을 권장 하고 있습니다.
    전체 시스템에서 사용 가능한 파일 핸들의 최대 값을 확인 해보겠습니다.
    [2010-12-09 04:06:03]-[root@calmmass:/proc/sys/fs]
    # cat /proc/sys/fs/file-max |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    104.857

    [ 매개변수 - net.Ipv4.Ip_local_port_range ] 이부분은 새 접속에서 사용할 수 있는 포트의 사용 가능 범위를 확장하는 의미를 가지고 있습니다.
    시스템에서 사용하는 포트번호는 /etc/services 파일에 구성 되어있습니다.
    [2010-12-09 04:06:24]-[root@calmmass:/proc/sys/fs]
    # tail /etc/services
    binkp 24554/udp # Binkley
    asp 27374/tcp # Address Search Protocol
    asp 27374/udp # Address Search Protocol
    .. 생략 ..

    매개변수의 값을 확인 해보겠습니다. 오라클은 1024 ~ 65000까지 허용하도록 권장 합니다.
    [2010-12-09 04:14:10]-[root@calmmass:/proc/sys/net/ipv4]
    # cat /proc/sys/net/ipv4/ip_local_port_range |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    32.768 61.000

    [ 매개변수 - net.core.rmem_default, net.core.rmem_max ] 일반적인 경우의 대용량 Serving을 위한 설정 합니다. 오라클은 기본적으로 대용량 데이터베이스를 기준으로 하기 때문에 기본 설정 된 값을 변경해야 합니다. 아래와 같은 경우에는 기본 설정 값입니다.
    [2010-12-09 04:18:03]-[root@calmmass:/proc/sys/net/core]
    # cat /proc/sys/net/core/rmem_default |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    65.535
    [2010-12-09 04:18:29]-[root@calmmass:/proc/sys/net/core]
    # cat /proc/sys/net/core/rmem_max |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    131.071

    [ 매개변수 - net.core.wmem_default, net.core.wmem_max] 소켓 출력 큐의 기본 값과 최대 값을 조정하기 위한 매개변수 입니다. 일반적인 경우의 대용량 Serving을 위한 설정이므로 역시 rmem_default, rmem_max의 값과 동일하게 구성해는 것이 바랍직 합니다. 기본적으로 설정 되어있는 값을 확인 해보겠습니다.
    # cat /proc/sys/net/core/wmem_default |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    65.535
    [2010-12-09 04:23:05]-[root@calmmass:/proc/sys/net/core]
    # cat /proc/sys/net/core/wmem_max |sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1.\2/;ta'
    131.071

    [ 커널 매개변수 반영 ]
    이상으로 매개변수의 용도와 설정값을 확인 하는 방법을 실습 했습니다.
    이번에는 커널 매개변수를 오라클 기준으로 변경해서 반영 해보도록 하겠습니다. 기본적으로는 위에 나와있는 설정 파일에 값을 변경 해도 되며, sysctl -w [매개변수명]=value 명령어를 이용 할 수 있습니다. 하지만 이번 문서에서는 리눅스의 장점을 살려 환경설정 파일로 매개변수를 반영 하겠습니다.

    오라클(바로가기) 설치 메뉴얼에는 아래와 같이 설정 하길 권장합니다.
    cat >> /etc/sysctl.conf <<EOF
    kernel.shmall = 2097152
    kernel.shmmax = 2147483648
    kernel.shmmni = 4096
    kernel.sem = 250 32000 100 128
    fs.file-max = 65536
    net.ipv4.ip_local_port_range = 1024 65000
    EOF
    /sbin/sysctl -p

    파라매터를 더 추가해보겠습니다. (강추 입니다.)
    kernel.shmmax=536870912
    kernel.shmmni=4096
    kernel.shmall=2097152
    kernel.sem=250 32000 100 128
    fs.file-max=65536
    net.ipv4.ip_local_port_range=1024 65000
    net.core.rmem_default=262144
    net.core.rmem_max=262144
    net.core.wmem_default=262144
    net.core.wmem_max=262144



    2. Shell Limit 설정

    오라클은 Linux 계정 별로 실행되는 프로세스와 열린 파일의 수를 제한하는 것을 권장합니다. 이를 위해, root 계정에서 아래 내용을 /etc/security/limits.conf 설정파일에 oracle10g 라는 특정 프로그램을 제어하는 방법을 설명 하겠습니다.

    [참고] /lib/security/pam_limits.so 모듈이 사용할 설정 파일이 /etc/security/limits.conf 파일 입니다. 그중에서 각각 설정에 대해서 아래와 같이 정리 하였습니다.
    domain : 제한을 할 대상으로써 사용자 이름이나 그룹 이름, 그리고 와일드 문자를 사용할 수 있습니다.
    type : 강하게 제한할 것인지 아니면 어느 정도 여유를 줄 것인지 결정을 합니다.(soft, hard)
    item : 제한을 할 항목입니다.
    - core : core 파일 사이즈(KB)
    - data : 최대 데이터 크기(KB)
    - fsize : 최대 파일 사이즈(KB)
    - memlock : 최대 locked-in-memory 주소 공간(KB)
    - nofile : 한번에 열 수 있는 최대 파일 수
    - rss : 최대 resident set size(KB)
    - stack : 최대 스택 사이즈(KB)
    - cpu : 최대 CPU 점유 시간(MIN)
    - nproc : 최대 프로세스 개수
    - as : 주소 공간 제한
    - maxlogins : 동시 로그인 최대 수
    - priority : 사용자 프로세스가 실행되는 우선 순위

    [참고] ulimit를 이용하여 리소스를 제한하는 방법입니다. ulimit는 PAM과는 조금 다르게 root계정까지도 제한을 할 수 있습니다. 여기에서 제한에는 두가지 종류가 있습니다.
    Soft Limit : 새로운 프로그램이 생성되면 디폴트로 적용되는 제한
    Hard Limit : Soft Limit에서 최대 늘릴 수 있는 제한
    단, Hard Limit는 root 유저에 의해 조정 가능하지만 무한히 늘리 수 없기 때문에 커널에서 해당 설정 값을 조정 후 늘릴 수 있습니다.


    [ oracle10g soft nproc 2047 ] oracle10g 라는 프로그램이 처음 수행시 기본값으로 사용하는 프로세스는 최대 2047로 제한한다는 의미 입니다.
    [ oracle10g hard nproc 16384 ] oracld10g 라는 프로그램이 수행시 사용하는 프로세스는 최대 16384로 제한 한다는 의미 입니다.
    [ oracle10g soft nofile 1024 ] oracle10g 라는 프로그램이 처음 수행시 기본값으로 사용하는 파일은 최대 1024로 제한 한다는 의미입니다.
    [ oracle10g hard nofile 65536 ] oracle10g 라는 프로그램이 수행시 사용하는 파일은 최대 65536으로 제한 한다는 의미 입니다.


    [의문점] 어떤 방식으로 해야 이상적인 설정 일까요?
    이상적인 설정은 실제로 oracle database가 가동 되고 나서 개수 제한으로 에러가 발생 하지 않게 하는 것이 가장 이상적인 설정이라고 볼 수 있습니다. 그래서 최대한 해당 프로세스의 개수가 어느정도인지 파일의 개수는 평균적으로 어느정도 사용하는지 파악후 설정 해주는것이 바람직합니다.

    그럼 특정 프로그램이 사용하는 프로세스의 개수를 테스트 해보도록 하겠습니다.
    [2010-12-09 05:38:43]-[root@calmmass:/proc/sys/net/core]
    # lsof -p 3094 |wc -l
    1370

    열려있는 파일 개수 확인 하는 방법
    [2010-12-09 05:39:01]-[root@calmmass:/proc/sys/net/core]
    # sysctl -a |grep file
    fs.file-max = 104857
    fs.file-nr = 1995 472 104857

    마지막으로 /etc/security/limits.conf 리소스 제한 설정파일에 oracle 설치시 필요한 설정 내역은 아래와 같습니다.
    oracle10g soft nproc 2047
    oracle10g hard nproc 16384
    oracle10g soft nofile 1024
    oracle10g hard nofile 65536




    Oracle을 각 OS별 설치하면서 느낀 점은 위와 같은 설정 내역이 조금씩 다르다는 점입니다.
    그렇다면 이러한 설정 내역을 모두 외우는 것은 무의미하고 어떤 작업에 의해서 설정을 해야 하는지를 먼저 파악하는 것이 우선이라고 생각 했습니다.
    각 설정 파일의 역활과 오라클이 구동 되었을때 사용하는 용도를 보면 조금 접근하기기 편하지 않을까 생각 해봅니다.

     

    Posted by redkite
    , |

    0017. INDEX 생성 가이드라인

    ☞ 12-5 b-tree index

    * 특성
    1. 컬럼값이 같으면 rowid 순으로 정렬
    2. null에는 인덱스가 생성되지 않는다.

    * 인덱스에 대한 DML 작업 효과

    Index 에는 pctused파라미터가 없다.


    이말은 인덱스를 삭제하고 남은 공간을 다시 활용하지 않는 다는 뜻이다.
    이런 이유 때문에 인덱스는 주기적으로 정리 되어야 하며 그렇게 하지 않으면 인덱스의 사이즈는 점점 증가 한다.

    ☞ 12-7 bitmap index

    index를 경유하면 모든 데이터를 읽지않고 원하는 범위의 데이터만 읽기 때문에 빠른 성능을 보장받을 수 있다. 하지만 그범위가 아주 넓은 경우에는 오히려 더 나쁜 성능을 나타내게 된다. 이처럼 B*tree inex의 단점은 cardinality가 낮은 속성에 대하여 취약하는 것이다.
    (색깔의 종류가 적을 수록 좋다)

    ☞ 12-9 비트리 인데스와 비트맵 인덱스 비교

    비트리 인덱스는 카디널리티가 높은 것에 유리하고 비트맵은 낮은것에 유리 함. 그리고 비트맵 인덱스는 비트맵 세그먼트 수준의 잠금을 사용하기 때문에 비트맵 인덱스의 키열을 갱신하면 더 많은 비용이 들지만 B트리 인덱스에서는 테이블의 각행에 해당하는 항목을 잠금으로 update 할 때 유리 . 비트맵은 부울 연산 식을 사용해서 부울 수식에 유리.
    OLTP(게시판이나 수시로 바끼는 것)환경에서는 B트리 인덱스가 유리. 대형 정적 테이블에서 복합 질의 사용하는 데이터 웨어하우스 환경에서는 비트맵이 유리.

    ☞ 12-10 일반 B 트리 인덱스 생성

    create index hr.employees_last_name_indx
    on hr.employees(last_name)
    pctfree 30
    storage (initial 200k next 200k
    pctincrease 0 maxextents 50)
    tablespace indx;


    last_name 열을 사용하여 employees 테이블에 인덱스를 생성



    주의) 밑에 tablespace indx;를 지정하지 않으면 동시에 사용자가 접속하면 index를 사용하면 lock이 걸리기 때문에 지정해 줘야함


    ☞ 12-13 인덱스 생성시 가이드라인

    create index emp_hiredate on emp(hiredate) pctfree 30 initrans 10;

    1. 인덱스를 사용하면 쿼리 속도는 빨라지나 DML 작업속도는 느려진다.

    2. 별도의 테이블 스페이스에 인덱스 세그먼트를 둔다.

    3. 대용량 테이블에 인덱스 생성시 nologging 옵션을 쓰는게 좋다.

    이유) 큰 인덱스의 경우 리두로그파일 생성을 방지하면 성능을 상당히 향상시킬수 있으므로

    4. 인덱스 생성시 initrans(각 블록에서 미리 할당하는 트랜잭션 항목수) 값을 테이블 보다 많이 줘야 한다.

    이유) 인덱스 항목은 자신이 인덱스하는 행보다 작기 때문에 인덱스 블록은 블록마다 많은 항목을 포함 해당 테이블보다 인덱스에 대한 initrans가 높아야 한다.

    ☞ 12-15 비트맵 인덱스 생성

    create bitmap index orders_region_id_idx
    on orders(region_id)
    pctfree 30
    storage(initial 200k next 200k
    pctincrease 0 maxextents 50)
    tablespace indx;

     

    ☞ 12-18 인덱스 저장 영역 매개변수 변경

    alter index employees_last_name_idx
    storage(next 400k
    maxextents 100);

     

    ☞ 12-20 인덱스 공간 할당 및 할당 해제

    alter index orders_region_id_idx
    allocate extent (size 200k
    datafile '/disk6/indx01.dbf');

     

    alter index orders_id_idx
    deallocate unused;


    테이블에 대량의 삽입 작업 전에 인덱스에 확장영역을 추가 할수 있으며 확장 영역을 추가하면 인덱스가 동적으로 할당 되어 성능 저하를 방지 할수 있다.


    ☞ 12-21 인덱스 재구축

    alter index orders_region_id_idx rebuild
    teablespace indx02;


    * 기존 인덱스를 데이터 소스로 사용하여 인덱스 구축
    * 기존 인덱스를 사용하면 다시 구축할 필요가 없으므로 성능 향상
    * 새 인덱스를 구축할 충분한 공간 필요
    * 새 인덱스를 구축하는 동안 quary에서 기존 인덱스를 사용 가능

    @ 재구축이 필요한 상황

    1. 인덱스가 있는 테이블 스페이스를 이동할때
    2. 인덱스 테이블을 alter table ........ move tablespace 명령을 사용하여 이동

    SQL> select table_name, tablespace_name from user_tables
    2 where table_name='EMP';

    TABLE_NAME TABLESPACE_NAME
    ------------------------------ ------------------------------
    EMP USERS

    SQL> create index emp_ename
    2 on emp(ename);

    인덱스가 생성되었습니다.

    SQL> alter table emp
    2 move tablespace tools;

    테이블이 변경되었습니다.

    SQL> select ename
    2 from emp
    3 where ename='SCOTT';


    select ename
    *
    1행에 오류:
    ORA-01502: 인덱스 'SCOTT.EMP_ENAME'또는 인덱스 분할영역은 사용할 수 없은
    상태입니다


    SQL> alter index emp_ename rebuild;

    인덱스가 변경되었습니다.

    SQL> select ename
    2 from emp
    3 where ename='SCOTT';

    ENAME
    ----------
    SCOTT

    SQL> @p.sql


    file toto.sql(이)가 기록되었습니다

    테이블이 잘렸습니다.

    1* select ename
    1* explain plan set statement_id='MyTest' for select ename

    해석되었습니다.


    Query Plan
    ---------------------------------------------------------------
    SELECT STATEMENT Cost =
    INDEX RANGE SCAN EMP_ENAME NON-UNIQUE

    실습2)

    1* alter index emp_ename rebuild reverse
    SQL> /

    인덱스가 변경되었습니다.

    SQL> select index_name, index_type
    2 from user_indexes;

    INDEX_NAME INDEX_TYPE
    ------------------------------ ---------------------------
    C_ID_DATE NORMAL
    C_ID_PK NORMAL
    DEPT200_DEPTNO_PK NORMAL
    EMP21_EMPNO_PK NORMAL
    EMP22_EMPNO_PK NORMAL
    EMP_BIT_JOB BITMAP
    EMP_ENAME NORMAL/REV
    EMP_HIREDATEON NORMAL
    SYS_C003006 NORMAL
    SYS_IL0000030314C00001$$ LOB

    10 개의 행이 선택되었습니다.

    bitmap 도 될까 ?

    ☞ 12-23 index 온라인으로 재구축

    alter index ordes_id_idx rebuild online;

    9i 에서 인덱스 재 구축 작업 동안 동시에 DML 작업 수행할수있는것

    SQL> update test1
    2 set sal=0
    3 where id=9993;

    2 행이 갱신되었습니다.

     

    다른세션에서 인덱스를 생성하려하거나 재구축 또는 삭제하려하는게 안된다.

    SQL> alter index test1_sal rebuild reverse;
    alter index test1_sal rebuild reverse
    *
    1행에 오류:
    ORA-00054: 자원이 사용중이고, NOWAIT가 지정되어 있습니다


    SQL> /

    인덱스가 변경되었습니다.

    SQL> /
    alter index test1_sal rebuild reverse
    *
    1행에 오류:
    ORA-00054: 자원이 사용중이고, NOWAIT가 지정되어 있습니다


    SQL> drop index test1_sal;
    drop index test1_sal
    *
    1행에 오류:
    ORA-00054: 자원이 사용중이고, NOWAIT가 지정되어 있습니다


    SQL> /

    인덱스가 삭제되었습니다.

    SQL> create index test1_sal
    2 on test1(sal);
    on test1(sal)
    *
    2행에 오류:
    ORA-00054: 자원이 사용중이고, NOWAIT가 지정되어 있습니다

    다음 실습은 sh 유져에서 작업할것이다. sh 계정을 활성화시키시요 ( 유쌤 ^^)

    SQL> create index sales_amount_sold
    2 on sales(amount_sold);

    인덱스가 생성되었습니다.

    다른세션에서

    SQL> update sales
    2 set amount_sold=0
    3 where prod_id=250;

    475 행이 갱신되었습니다.

    책에 나온 내용과 테스트가 틀리다. 책이 틀린것 같다. ( 또 오라클이 오버했네요. 안되는거 된다고 하고...)

    그런데 재구축은 책이 맞습니다.

    먼저 online 을 붙이지 않고 실습하고

    alter index slaes_amount_sold rebuild ;

    다른세션에서

    SQL> update sales
    2 set amount_sold=0
    3 where prod_id=250;

    475 행이 갱신되었습니다.

    online 붙이고 실습하라

    alter index slaes_amount_sold rebuild online;

    SQL> update sales
    2 set amount_sold=0
    3 where prod_id=250;

    475 행이 갱신되었습니다.

    ☞ 12-23 index 유효성 검사

    SQL> analyze index sales_amount_sold validate structure;

    SQL> select blocks, pct_used, distinct_keys
    2 , lf_rows, del_lf_rows
    3 from index_stats;

    BLOCKS PCT_USED DISTINCT_KEYS LF_ROWS DEL_LF_ROWS
    ---------- ---------- ------------- ---------- -----------
    2816 90 6755 1016271 0

    del_lf_rows 와 lf_rows 의 비율이 30% 를 초과하는 경우에 인덱스를 재구성한다


    ☞ 12-23 사용되는 index 와 안되는 인덱스 구별 (9i 뉴피쳐)

    SQL> alter index sales_amount_sold monitoring usage;

    인덱스가 변경되었습니다.

    SQL> select * from v$object_usage;

    다른세션에서

    select *
    from sales
    where amount_sold=250
    /

    그리고 다시

    SQL> select * from v$object_usage;

    보면 use 가 yes 로 나올것이다.

    ☞ 12-24 인덱스 병합

    alter index orders_id_idx coalesce;

     

    coalesce 작업을 수행하기 전 첫번때 두개의 최하위 블록은 50%가 채워진 상태이고, 인덱스가 단편화 되었으므로 병합하여 하나로 합친다.

    ☞ 12-25 인덱스 및 유효성 검사

    ANALYZE INDEX orders_region_id_idx
    VALIDATE STRUCTURE;

    인덱스를 분석하여 다음을 수행
    * 모든 인덱스 블록에 대해 손상된 블록을 확인. 이명령을 수행해도 인덱스 항목이 테이블의 데이터에 대응되는지 여부는 확인되니 않음

    * index_stats 뷰를 인덱스 정보로 채움

    SELECT (del_lf_rows_len/ lf_lows_len) * 100 as balancing from index_stats;

    15% 만 rebuilding 함

    ☞ 12-29 사용 되지 않은 익덱스 식별

    *인덱스 사용에 대한 모니터를 시작하는 방법

    ALTER INDEX hr.dept_id_idx
    MONITORING USAGE

    * 인덱스 사용에 대한 모니터를 중지하는 방법

    ALTER INDEX hr.dept_id_idx
    NOMONITORING USAGE

    ==>인덱스 사용에 대한 통계를 수집하여 V$OBJECT_USAGE에 표시

    SELECT index_name, used from V$OBJECT_USAGE;

    Posted by redkite
    , |

    0016. Oracle 8i Nosegment Index vs. Oracle 11g Invisible Index

    Oracle 11g New Feature에 새로운 이야기들을 많이 들었을 것이다.
    이중 유난히 나의 관심을 끄는 한 가지가 Invisiblie Index이다. Invisilble Index란 말 그대로 "보이지" 않는 인덱스를 의미한다. 더 정확하게 말하면 인덱스 세그먼트로서 물리적으로 존재는 하지만 Optimizer에게는 보이지 않는 인덱스를 의미한다.

    테이블에 새로운 인덱스를 추가하면 Optimizer의 비용(Cost) 계산에 영향을 주어 기존 쿼리의 실행 계획에 영향을 주게 된다. 물론 이것은 바람직한 것이고, 인덱스의 존재 이유이기도 하다.

    하지만, 간혹 인덱스를 추가했을 때 이 인덱스로 인해 성능 개선이 이루어질 것인지만을 테스트해고 싶은 경우가 있다. 이런 경우에는 어떻게 해야할까?

    잘 알려지지 않은 사실이지만, Oracle 8i에서 Virtual Index 기능이 추가되었다. Virtual Index란 말 그대로 가상의 인덱스로 실제로는 인덱스가 존재하지 않지만, 인덱스가 존재하는 것처럼 데이터를 조작하는 것을 의미한다. 이런 의미에서 흔히 Fake Index(가짜 인덱스)라고 부르기도 한다. 아래 테스트 스크립트를 보자.

    select * from v$version;
    --------------------------------------------------------
    BANNER
    Oracle8i Enterprise Edition Release 8.1.7.0.0 - 64bit Production
    PL/SQL Release 8.1.7.0.0 - Production
    CORE 8.1.7.0.0 Production
    TNS for HPUX: Version 8.1.7.0.0 - Development
    NLSRTL Version 3.4.1.0.0 - Production

    -- Table 생성
    create table t_noseg(id int, id2 int);

    -- Clustering Factor를 달리 하기 위해 id, id2의 순서를 다르게 삽입한다.
    insert into t_noseg
    select rownum, decode(mod(rownum,2),1,50000-rownum+1,rownum) from all_objects t1, all_objects t2
    where rownum <= 50000

    commit;

    -- Virtual Index를 생성한다. (NOSEGMENT 속성 부여)
    -- id 컬럼에 대한 인덱스는 Clustering Factor가 매우 우수하다.
    -- 하지만 id2 컬럼에 대한 인덱스는 Clustering Factor가 매우 불량하다.
    create index t_noseg_idx on t_noseg(id) nosegment;
    create index t_noseg_idx2 on t_noseg(id2) nosegment;

    -- 통계 정보를 생성한다.
    exec dbms_stats.gather_table_stats(user,'T_NOSEG', cascade=>true);

    -- Virtual Index는 실제로는 사용되지 않으며 실행 계획을 테스트하는 목적으로만 사용된다.
    -- _use_nosegment_indexes 값을 true로 변경하면 실행 계획에 반영할 수 있다.
    alter session set "_use_nosegment_indexes" = true;

    -- 다음과 같이 id 컬럼에 대해 가상의 인덱스를 선택하는 실행 계획이 수립됨을 확인할 수 있다.
    -- Cost가 4이고, Index는 Analyze가 되지 않았다는 사실에 주목하자.
    select * from t_noseg
    where id between 1 and 100;

    SELECT STATEMENT CHOOSE-Cost : 4
    TABLE ACCESS BY INDEX ROWID MAXGAUGE.T_NOSEG(1) Analyzed : 20080113
    INDEX RANGE SCAN MAXGAUGE.T_NOSEG_IDX(NU) (ID) NOT ANALYZED

    -- id2 컬럼에 대해서도 역시 인덱스를 선택하는 실행 계획이 수립된다.
    -- 하지만, Clustering Factor에 의한 비용 증가는 계산되지 않는다.
    select * from t_noseg
    where id2 between 1 and 100;

    SELECT STATEMENT CHOOSE-Cost : 4
    TABLE ACCESS BY INDEX ROWID MAXGAUGE.T_NOSEG(1) Analyzed : 20080113
    INDEX RANGE SCAN MAXGAUGE.T_NOSEG_IDX2(NU) (ID2) NOT ANALYZED

    -- 세그먼트가 존재하는가?
    select count(*) from dba_segments where segment_name = 'T_NOSEG_IDX';
    ----------------------------------------------------------------
    0

    select count(*) from dba_objects where object_name = 'T_NOSEG_IDX';
    ----------------------------------------------------------------
    1

    위의 테스트 결과를 보면 다음과 같은 사실을 추론할 수 있다.
    - Virtual Index는 실제로는 세그먼트가 생성되지 않으며, 딕셔너리에만 존재하는 인덱스이다.
    - 따라서 실행 계획을 추측해보는데는 도움이 되지만, 실제로 실행에 반영할 수는 없다.
    - 실제 세그먼트가 존재하지 않기 때문에 통계 정보를 수집할 수 없다. 따라서 Clustering Factor와 같은 중요한 요소는 계산되지 않는다. 이는 비용 계산에 오류를 초래할 수 있다.

    Virtual Index는 오라클 내부적으로만 사용될 목적으로 만들어졌으며 대중적으로 공개가 되어 있지 않다.

    Oracle 11g는 Virtual Index를 계승 발전시켜서 Invisible Index라는 전혀 새로운 개념을 소개하고 있다. Invisible Index는 Virtual Index와는 달리 실제로 세그먼트 형태로 존재하는 인덱스이다. 다만 Optimizer에게 보이지 않을 뿐이다. 아래 테스트 스크립트를 보자.

    -- Table 생성
    create table t_invisible(id int, id2 int);

    -- 역시 Clustering Factor를 달리 하기 위해 id, id2의 순서를 다르게 삽입한다.
    insert into t_invisible
    select rownum, decode(mod(rownum,2),1,50000-rownum+1,rownum) from all_objects t1, all_objects t2
    where rownum <= 50000

    commit;

    -- Invisible Index를 생성한다.
    create index t_invisible_idx on t_invisible(id) invisible;
    create index t_invisible_idx2 on t_invisible(id2) invisible;

    -- Invisible Index를 사용하게끔 변경한다. 이렇게 해야만 통계 정보가 수집된다.
    alter session set optimizer_use_invisible_indexes=true;

    -- 통계 정보 수집
    exec dbms_stats.gather_table_stats(user,'T_INVISIBLE', cascade=>true);

    -- 다음과 같이 id 컬럼에 대해 가상의 인덱스를 선택하는 실행 계획이 수립됨을 확인할 수 있다.
    -- Cost가 3이고, Index가 Analyze되었음에 유의하자.
    select * from t_invisible
    where id between 1 and 100;

    SELECT STATEMENT ALL_ROWS-Cost : 3
    TABLE ACCESS BY INDEX ROWID UKJA.T_INVISIBLE(1)
    INDEX RANGE SCAN UKJA.T_INVISIBLE_IDX (ID)

    -- id2 컬럼에 대해서는 Full Table Scan을 사용하다.
    -- id2 컬럼은 Clustering Factor가 매우 불량하기 때문에 이것이 비용에 반영된 결과이다.
    select * from t_invisible
    where id2 between 1 and 100;

    SELECT STATEMENT ALL_ROWS-Cost : 21
    TABLE ACCESS FULL UKJA.T_INVISIBLE(1)

    -- 실제 id2 컬럼에 대해 인덱스를 사용하면 비용이 어떻게 계산되지는 확인해보면 아래와 같다.
    -- 비용이 102로 Full Table Scan보다 오히려 비싸게 계산되는 것을 확인할 수 있다.
    select /*+ index(t_invisible t_invisible_idx2) */* from t_invisible
    where id2 between 1 and 100;

    SELECT STATEMENT ALL_ROWS-Cost : 102
    TABLE ACCESS BY INDEX ROWID UKJA.T_INVISIBLE(1)
    INDEX RANGE SCAN UKJA.T_INVISIBLE_IDX2 (ID2)

    -- 세그먼트가 존재하는가?
    select count(*) from dba_segments where segment_name = 'T_INVISIBLE_IDX';
    -----------------------------------------------------------------
    1

    Invisible Index는 Virtual Index와는 달리 실제로 존재하는 인덱스이고 따라서 통계 정보가 수집된다. 따라서 보다 정확한 테스트를 수행할 수 있다. 테스트후 반영여부가 결정되면 다음과 같이 손쉽게 Visible Index로 전환할 수 있다.

    alter index t_invisible_idx visible;

    위의 간단한 테스트 결과를 보면 Invisible Index가 기존의 Virtual Index에 비해서 훨씬 유용한 기능을 제공하는지 잘 알 수 있다. 아무쪼록 많이 활용되었으면 한다.

    (참고) Invisible Index가 존재하는 테이블/인덱스에 대해 통계 정보를 수집하려고 하면 ORA-904: Invalid Column 에러가 발생한다. 이것은 11g R1의 버그이다. 패치가 나오기 전까지는 optimizer_use_invisible_indexes 값을 true로 변경한 후 통계 정보를 수집해야 한다.

    Posted by redkite
    , |

    0015. 데이터베이스 아키텍쳐

    1. 아키텍처 개관

    가. oracle 아키텍처

    Database : 데이터 집합(Datafile, Redo Log File, Control File 등)

    Instance : SGA 공유 메모리 영역과 이를 액세스하는 프로세스 집합

    기본적으로 하나의 인스턴스가 하나의 데이터베이스만 액세스하지만, RAC(Real Application Cluster) 환경에서는 여러 인스턴스가 하나의 데이터베이스를 액세스할 수 있다. 하나의 인스턴스가 여러 데이터베이스를 액세스할 수는 없다.

    나. SQL Server 아키텍처

    데이터베이스 하나를 만들 때마다 주(Primary 또는 Main) 데이터 파일과 트랜잭션 로그 파일이 하나씩 생기는데, 전자는 확장자가 mdf이고 후자는 ldf이다. 저장할 데이터가 많으면 보조(Non-Primary) 데이터 파일을 추가할 수 있으며, 확장자는 ndf이다.

    2. 프로세스

    프로세스는 서버 프로세스(Server Processes)와 백그라운드 프로세스(Background Processes) 집합으로 나뉨. 서버 프로세스는 전면에 나서 사용자가 던지는 각종 명령을 처리하고, 백그라운드 프로세스는 뒤에서 묵묵히 주어진 역할을 수행한다.(SQL Server는 쓰레드(Thread) 기반 아키텍처)

    가. 서버 프로세스(Server Processes)

    서버 프로세스는 사용자 프로세스와 통신하면서 사용자의 각종 명령을 처리하며, SQL Server에선 Worker 쓰레드가 같은 역할을 담당한다.

    SQL을 파싱하고 필요하면 최적화를 수행하며, 커서를 열어 SQL을 실행하면서 블록을 읽고, 읽은 데이터를 정렬해서 클라이언트가 요청한 결과집합을 만들어 네트워크를 통해 전송하는 일련의 작업을 모두 서버 프로세스가 처리.

    스스로 처리하도록 구현되지 않은 기능, 이를테면 데이터 파일로부터 DB 버퍼 캐시로 블록을 적재하거나 Dirty 블록을 캐시에서 밀어냄으로써 Free 블록을 확보하는 일, 그리고 Redo 로그 버퍼를 비우는 일 등은 OS, I/O 서브시스템, 백그라운드 프로세스가 대신 처리하도록 시스템 Call을 통해 요청. Oracle에는 전용/공유 서버 방식 두 가지가 있다.

    1) 전용 서버(Dedicated Server) 방식

    서버 프로세스가 단 하나의 사용자 프로세스를 위해 전용(Dedicated) 서비스를 제공.

    SQL을 수행할 때마다 연결 요청을 반복하면 서버 프로세스의 생성과 해제도 반복하게 되므로 DBMS에 매우 큰 부담을 주고 성능을 크게 떨어뜨림(OLTP성 애플리케이션에선 Connection Pooling 기법을 필수적으로 사용해야 한다.)

     

    2) 공유 서버(Shared Server) 방식

    하나의 서버 프로세스를 여러 사용자 세션이 공유하는 방식으로서 여러 개의 서버 프로세스를 띄어 놓고 이를 공유해서 사용.

    사용자 프로세스는 서버 프로세스와 직접 통신하지 않고 Dispatcher 프로세스를 거친다.

    사용자 명령이 Dispatcher에게 전달되면 Dispatcher는 이를 SGA에 있는 요청 큐(Request Queue)에 등록.

    이후 가장 먼저 가용해진 서버 프로세스가 요청 큐에 있는 사용자 명령을 꺼내서 처리하고

    그 결과를 응답 큐(Response Queue)에 등록한다.

    응답 큐를 모니터링하던 Dispatcher가 응답 결과를 발견하면 사용자 프로세스에게 전송해 준다.

     

    나. 백그라운드 프로세스(Background Processes)

    3. 파일 구조

    가. 데이터 파일

    1) 블록(=페이지)

    대부분 DBMS에서 I/O는 블록 단위.(Oracle은 '블록(Block)', SQL Server는 '페이지(Page)')

    Oracle은 2KB, 4KB, 8KB, 16KB, 32KB, 64KB의 다양한 블록 크기를 사용가능

    SQL Server에선 8KB 단일 크기를 사용한다.

    *블록 단위로 I/O 한다는 것은, 하나의 레코드에서 하나의 칼럼만을 읽으려 할 때도 레코드가 속한 블록 전체를 읽게 됨을 뜻함

    SQL 성능을 좌우하는 가장 중요한 성능지표는 액세스하는 블록 개수.

     

    2) 익스텐트(Extent)

    테이블 스페이스로부터 공간을 할당하는 단위. 테이블이나 인덱스에 데이터를 입력하다가 공간이 부족해지면 해당 오브젝트가 속한 테이블 스페이스(물리적으로는 데이터 파일)로부터 추가적인 공간을 할당받는데, 이때 정해진 익스텐트 크기의 연속된 블록을 할당받는다.

    익스텐트 내 블록은 논리적으로 인접하지만, 익스텐트끼리 서로 인접하지는 않는다.

    (Oracle은 다양한 크기의 익스텐트를 사용하지만, SQL Server에선 8개 페이지의 익스텐트만을 사용.)

    Oracle은 한 익스텐트에 속한 모든 블록을 단일 오브젝트가 사용하지만, SQL Server에서는 2개 이상 오브젝트가 나누어 사용할 수도 있다.

    SQL Server는 다음 2가지 타입의 익스텐트를 사용.

    • 균일(Uniform) 익스텐트 : 64KB 이상의 공간을 필요로 하는 테이블이나 인덱스를 위해 사용되며, 8개 페이지 단위로 할당된 익스텐트를 단일 오브젝트가 모두 사용한다.
    • 혼합(Mixed) 익스텐트 : 한 익스텐트에 할당된 8개 페이지를 여러 오브젝트가 나누어 사용하는 형태다. 모든 테이블이 처음에는 혼합 익스텐트로 시작하지만 64KB를 넘으면서 2번째부터 균일 익스텐트를 사용하게 된다.

     

    3) 세그먼트

    세그먼트는 테이블, 인덱스, Undo처럼 저장공간을 필요로 하는 데이터베이스 오브젝트다. (한개 이상의 익스텐트 사용)

    ((ex 테이블,인덱스 )

    다른 오브젝트는 세그먼트와 1:1 대응 관계를 갖지만 파티션은 1:M 관계를 갖음.

    파티션 테이블(또는 인덱스)을 만들면, 내부적으로 여러 개의 세그먼트가 만들어진다.

    한 세그먼트는 자신이 속한 테이블 스페이스 내 여러 데이터 파일에 걸쳐 저장될 수 있음.

    세그먼트에 할당된 익스텐트가 여러 데이터 파일에 흩어져 저장되는 것이며, 그래야 디스크 경합을 줄이고 I/O 분산 효과를 얻을 수 있다.

     

    4) 테이블 스페이스

    테이블 스페이스는 세그먼트를 담는 콘테이너로서, 여러 데이터 파일로 구성된다.

    사용자는 세그먼트를 위한 테이블 스페이스를 지정할 뿐, 실제 값을 저장할 데이터 파일을 선택하고 익스텐트를 할당하는 것은 DBMS의 몫. 각 세그먼트는 정확히 한 테이블 스페이스에만 속하지만, 한 테이블 스페이스에는 여러 세그먼트가 존재할 수 있다.

    특정 세그먼트에 할당된 모든 익스텐트는 해당 세그먼트와 관련된 테이블 스페이스 내에서만 찾아진다. (한 세그먼트가 여러 테이블 스페이스에 걸쳐 저장될 수는 없음.)

    한 세그먼트가 여러 데이터 파일에 걸쳐 저장될 수는 있다. 한 테이블 스페이스가 여러 데이터 파일로 구성되기 때문.

     

     

    나. 임시 데이터 파일

    임시(Temporary) 데이터 파일은 특별한 용도로 사용된다. 대량의 정렬이나 해시 작업을 수행하다가 메모리 공간이 부족해지면 중간 결과집합을 저장하는 용도다. ( Redo 정보를 생성하지 않기 때문에 나중에 파일에 문제가 생겼을 때 복구되지 않는다. 따라서 백업할 필요도 없다.)

    Oracle에선 임시 테이블 스페이스를 여러 개 생성해 두고, 사용자마다 별도의 임시 테이블 스페이스를 지정해 줄 수도 있다.

    create temporary tablespace big_temp tempfile '/usr/local/oracle/oradata/ora10g/big_temp.dbf' size 2000m;

    alter user scott temporary tablespace big_temp;

    SQL Server는 단 하나의 tempdb 데이터베이스를 사용한다. tempdb는 전역 리소스로서 시스템에 연결된 모든 사용자의 임시 데이터를 여기에 저장한다.

    다. 로그 파일

    DB 버퍼 캐시에 가해지는 모든 변경사항을 기록하는 파일

    Oracle : 'Redo 로그'

    SQL Server는 '트랜잭션 로그'.

    변경된 메모리 버퍼 블록을 디스크 상의 데이터 블록에 기록하는 작업은 Random I/O 방식으로 이루어지기 때문에 느리다.

    반면 로그 기록은 Append 방식으로 이루어지기 때문에 상대적으로 매우 빠르다.

    DBMS는 버퍼 블록에 대한 변경사항을 건건이 데이터 파일에 기록하기보다 우선 로그 파일에 Append 방식으로 빠르게 기록하는 방식을 사용한다. 그러고 나서 버퍼 블록과 데이터 파일 간 동기화는 적절한 수단(DBWR, Checkpoint)을 이용해 나중에 배치(Batch) 방식으로 일괄 처리한다.

    'Fast Commit' : 사용자의 갱신내용이 메모리상의 버퍼 블록에만 기록된 채 아직 디스크에 기록되지 않았더라도 Redo 로그를 믿고

    빠르게 커밋을 완료

    • Online Redo 로그
      캐시에 저장된 변경사항이 아직 데이터 파일에 기록되지 않은 상태에서 정전 등으로 인스턴스가 비정상 종료되면, 그때까지의 작업내용을 모두 잃게 된다. 이러한 트랜잭션 데이터의 유실에 대비하기 위해 Oracle은 Online Redo 로그를 사용한다.
    • '캐시 복구' " 마지막 체크포인트 이후부터 사고 발생 직전까지 수행되었던 트랜잭션들을 Redo 로그를 이용해 재현
    • Online Redo 로그는 최소 두 개 이상의 파일로 구성된다. 현재 사용 중인 파일이 꽉 차면 다음 파일로 로그 스위칭(log switching)이 발생하며, 계속 로그를 써 나가다가 모든 파일이 꽉 차면 다시 첫 번째 파일부터 재사용하는 라운드 로빈(round-robin) 방식을 사용
    • 트랜잭션 로그
      트랜잭션 로그는 Oracle의 Online Redo 로그와 대응되는 SQL Server의 로그 파일이다. 주 데이터 파일마다, 즉 데이터베이스마다 트랜잭션 로그 파일이 하나씩 생기며, 확장자는 ldf이다. 트랜잭션 로그 파일은 내부적으로 '가상 로그 파일'이라 불리는 더 작은 단위의 세그먼트로 나뉘며, 이 가상 로그 파일의 개수가 너무 많아지지 않도록(즉, 조각화가 발생하지 않도록) 옵션을 지정하는 게 좋다. 예를 들어, 로그 파일을 애초에 넉넉한 크기로 만들어 자동 증가가 발생하지 않도록 하거나, 어쩔 수 없이 자동 증가한다면 증가하는 단위를 크게 지정하는 것이다.
    • Archived(=Offline) Redo 로그
      Archived Redo 로그는 Oracle에서 Online Redo 로그가 재사용되기 전에 다른 위치로 백업해 둔 파일을 말한다. 디스크가 깨지는 등 물리적인 저장 매체에 문제가 생겼을 때 데이터베이스(또는 미디어) 복구를 위해 사용된다. 참고로, SQL Server에는 Archived Redo 로그에 대응되는 개념이 없다.

    4. 메모리 구조

    메모리 구조는 시스템 공유 메모리 영역과 프로세스 전용 메모리 영역으로 구분된다.

    • 시스템 공유 메모리 영역
      시스템 공유 메모리는 말 그대로 여러 프로세스(또는 쓰레드)가 동시에 액세스할 수 있는 메모리 영역.
    • Oracle에선 'System Global Area(SGA)', SQL Server에선 'Memory Pool'이라고 부른다
    • DB 버퍼 캐시, 공유 풀, 로그 버퍼가 있다.
    • 공유 메모리 영역은 그 외에 Large 풀(Large Pool), 자바 풀(Java Pool)
    • 시스템 구조와 제어 구조를 캐싱하는 영역도 포함. 시스템 공유 메모리 영역은 여러 프로세스에 공유되기 때문에 내부적으로 래치(Latch), 버퍼 Lock, 라이브러리 캐시 Lock/Pin 같은 액세스 직렬화 메커니즘이 사용
    • 프로세스 전용 메모리 영역
      서버 프로세스가 자신만의 전용 메모리 영역 'Process Global Area(PGA)'라고 부르며, 데이터를 정렬하고 세션과 커서에 관한 상태 정보를 저장하는 용도로 사용.
    • 쓰레드(Thread) 기반 아키텍처를 사용하는 SQL Server는 프로세스 전용 메모리 영역을 갖지 않는다. 쓰레드는 전용 메모리 영역을 가질 수 없고, 부모 프로세스의 메모리 영역을 사용하기 때문이다.

    가. DB 버퍼 캐시(DB Buffer Cache)

    DB 버퍼 캐시는 데이터 파일로부터 읽어 들인 데이터 블록을 담는 캐시 영역이다.

    인스턴스에 접속한 모든 사용자 프로세스는 서버 프로세스를 통해 DB 버퍼 캐시의 버퍼 블록을 동시에(내부적으로는 버퍼 Lock을 통해 직렬화) 액세스할 수 있음.

    일부 Direct Path Read 메커니즘이 작동하는 경우를 제외하면, 모든 블록 읽기는 버퍼 캐시를 통해 이루어진다. 즉, 읽고자 하는 블록을 먼저 버퍼 캐시에서 찾아보고 없을 때 디스크에서 읽는다. 디스크에서 읽을 때도 먼저 버퍼 캐시에 적재한 후에 읽는다.

    데이터 변경도 버퍼 캐시에 적재된 블록을 통해 이루어지며, 변경된 블록(Dirty 버퍼 블록)을 주기적으로 데이터 파일에 기록하는 작업은 DBWR 프로세스의 몫이다. 디스크 I/O는 물리적으로 액세스 암(Arm)이 움직이면서 헤드를 통해 이루어지는 반면, 메모리 I/O는 전기적 신호에 불과하기 때문에 디스크 I/O에 비교할 수 없을 정도로 빠르다. 디스크에서 읽은 데이터 블록을 메모리 상에 보관해 두는 기능이 모든 데이터베이스 시스템에 필수적인 이유다.

     

    1) 버퍼 블록의 상태

    모든 버퍼 블록은 아래 세 가지 중 하나의 상태에 놓인다.

    • Free 버퍼 : 인스턴스 기동 후 아직 데이터가 읽히지 않아 비어 있는 상태(Clean 버퍼)이거나, 데이터가 담겼지만 데이터 파일과 서로 동기화돼 있는 상태여서 언제든지 덮어 써도 무방한 버퍼 블록을 말한다. 데이터 파일로부터 새로운 데이터 블록을 로딩하려면 먼저 Free 버퍼를 확보해야 한다. Free 상태인 버퍼에 변경이 발생하면 그 순간 Dirty 버퍼로 상태가 바뀐다.
    • Dirty 버퍼 : 버퍼에 캐시된 이후 변경이 발생했지만, 아직 디스크에 기록되지 않아 데이터 파일 블록과 동기화가 필요한 버퍼 블록을 말한다. 이 버퍼 블록들이 다른 데이터 블록을 위해 재사용되려면 디스크에 먼저 기록되어야 하며, 디스크에 기록되는 순간 Free 버퍼로 상태가 바뀐다.
    • Pinned 버퍼 : 읽기 또는 쓰기 작업이 현재 진행 중인 버퍼 블록을 말한다.

    2) LRU 알고리즘

    버퍼 캐시는 유한한 자원이므로 모든 데이터를 캐싱해 둘 수 없다. 따라서 모든 DBMS는 사용빈도가 높은 데이터 블록 위주로 버퍼 캐시가 구성되도록 LRU(least recently used) 알고리즘을 사용한다. 모든 버퍼 블록 헤더를 LRU 체인에 연결해 사용빈도 순으로 위치를 옮겨가다가, Free 버퍼가 필요해질 때면 액세스 빈도가 낮은 쪽(LRU end) 데이터 블록부터 밀어내는 방식이다.

    나. 공유 풀(Shared Pool)

    공유 풀은 딕셔너리 캐시와 라이브러리 캐시로 구성되며, 버퍼 캐시처럼 LRU 알고리즘을 사용한다. SQL Server에서 같은 역할을 하는 메모리 영역을 '프로시저 캐시(Procedure Cache)'라고 부른다.

    • 딕셔너리 캐시
      데이터베이스 딕셔너리(Dictionary)는 테이블, 인덱스 같은 오브젝트는 물론 테이블 스페이스, 데이터 파일, 세그먼트, 익스텐트, 사용자, 제약에 관한 메타 정보를 저장하는 곳.
    • 라이브러리 캐시
      라이브러리 캐시(Library Cache)는 사용자가 수행한 SQL문과 실행계획, 저장 프로시저를 저장해 두는 캐시영역.

     

    실행계획(execution plan) : 사용자가 SQL 명령어를 통해 결과집합을 요청하면 이를 최적으로(→가장 적은 리소스를 사용하면서 가장 빠르게) 수행하기 위한 처리 루틴을 생성

     

    하드 파싱(Hard Parsing) :쿼리 구문을 분석해서 문법 오류 및 실행 권한 등을 체크하고, 최적화(Optimization) 과정을 거쳐 실행계

    획을 만들고, SQL 실행엔진이 이해할 수 있는 형태로 포맷팅하는 전 과정.

    (특히 최적화 과정은 하드 파싱을 무겁게 만드는 가장 결정적 요인)

    다. 로그 버퍼(Log Buffer)

    로그 엔트리도 파일에 곧바로 기록하는 것이 아니라 먼저 로그 버퍼에 기록한다.

    (건건이 디스크에 기록하기보다 일정량을 모았다가 기록하면 훨씬 빠르기 때문)

    서버 프로세스가 데이터 블록 버퍼에 변경을 가하기 전에 Redo 로그 버퍼에 먼저 기록해 두면 주기적으로 LGWR 프로세스가 Redo 로그 파일에 기록한다.

    (Oracle의 Redo 로그, Redo 로그 버퍼와 대비되는 개념이 SQL Server의 트랜잭션 로그, 로그 캐시)

    변경이 가해진 Dirty 버퍼를 데이터 파일에 기록하기 전에 항상 로그 버퍼를 먼저 로그 파일에 기록해야만 하는데, 그 이유는 인스턴스 장애가 발생할 때면 로그 파일에 기록된 내용을 재현해 캐시 블록을 복구하고, 최종적으로 커밋되지 않은 트랜잭션은 롤백해야 한다.

    이때, 로그 파일에는 없는 변경내역이 이미 데이터 파일에 기록돼 있으면 사용자가 최종 커밋하지 않은 트랜잭션이 커밋되는 결과를 초래하기 때문.

    정리 : 버퍼 캐시 블록을 갱신하기 전에 변경사항을 먼저 로그 버퍼에 기록해야 하며, Dirty 버퍼를 디스크에 기록하기 전에 해당 로그 엔트리를 먼저 로그 파일에 기록해야 하는데, 이를 'Write Ahead Logging'이라고 한다.

    로그 파일에 기록했음이 보장돼야 안심하고 커밋을 완료할 수 있다.

    라. PGA(Process Global Area)

    PGA는 다른 프로세스와 공유되지 않는 독립적인 메모리 공간으로서, 래치 메커니즘이 필요 없어 똑같은 개수의 블록을 읽더라도 SGA 버퍼 캐시에서 읽는 것보다 훨씬 빠르다.

    • User Global Area(UGA)
    • 각 세션을 위한 독립적인 메모리 공간이 필요해지는데, 이를 'UGA(User Global Area)'라고함
    • 전용서버 : 프로세스와 세션이 1:1관계(PGA에 할당)
    • 공유서버 : 프로세스와 세션이 1:M관계(SGA 라지풀설정지 라지풀 또는 Shared pool에 할당)
    • Call Global Area(CGA)
      PGA에 할당되는 메모리 공간으로는 CGA도 있다. Oracle은 하나의 데이터베이스 Call을 넘어서 다음 Call까지 계속 참조되어야 하는 정보는 UGA에 담고, Call이 진행되는 동안에만 필요한 데이터는 CGA에 담는다.
    • CGA는 Parse Call, Execute Call, Fetch Call마다 매번 할당받는다. Call이 진행되는 동안 Recursive Call이 발생하면 그 안에서도 Parse, Execute, Fetch 단계별로 CGA가 추가로 할당된다. CGA에 할당된 공간은 하나의 Call이 끝나자마자 해제돼 PGA로 반환된다.
    • Sort Area
      데이터 정렬을 위해 사용되는 영역.
    • 소트 오퍼레이션이 진행되는 동안 공간이 부족해질 때마다 청크(Chunk) 단위로 조금씩 할당.
    • 세션마다 사용할 수 있는 최대 크기를 예전에는 sort_area_size 파라미터로 설정하였으나, 9i부터는 새로 생긴 workarea_size_policy 파라미터를 auto(기본 값)로 설정하면 Oracle이 내부적으로 결정.

    PGA 내에서 Sort Area가 할당되는 위치는 SQL문 종류와 소트 수행 단계에 따라 다르다.

    DML 문장은 하나의 Execute Call 내에서 모든 데이터 처리를 완료하므로 Sort Area가 CGA에 할당된다.

    SELECT 문장의 경우, 수행 중간 단계에 필요한 Sort Area는 CGA에 할당되고, 최종 결과집합을 출력하기 직전 단계에 필요한 Sort Area는 UGA에 할당된다.

    쓰레드(Thread) 기반 아키텍처를 사용하는 SQL Server는 프로세스 전용 메모리 영역을 갖지 않는다. 대신, 데이터 정렬은 Memory Pool 안에 있는 버퍼 캐시에서 수행하며, 세션 관련 정보는 Memory Pool 안에 있는 Connection Context 영역에 저장한다.

    5. 대기 이벤트

    DBMS 내부에서 활동하는 수많은 프로세스 간에는 상호작용이 필요하며, 그 과정에서 다른 프로세스가 일을 마칠 때까지 기다려야만 하는 상황이 자주 발생.

    그때마다 해당 프로세스는 자신이 일을 계속 진행할 수 있는 조건이 충족될 때까지 수면(sleep) 상태로 대기하는데, 그 기간에 정해진 간격으로(1초, 3초 등) 각 대기 유형별 상태와 시간 정보가 공유 메모리 영역에 저장.

    대개 누적치만 저장되지만, 사용자가 원하면(10046 이벤트 트레이스를 활성화하면) 로그처럼 파일로 기록해 주기도 한다.

    이러한 대기 정보를 Oracle에서는 '대기 이벤트(Wait Event)'라고 부르며, SQL Server에서는 '대기 유형(Wait Type)'이라고 부른다.

    세션 또는 시스템 전체에 발생하는 병목 현상과 그 원인을 찾아 문제를 해결하는 방법과 과정을 다루는 이 방법론은, 데이터베이스 서버의 응답 시간을 서비스 시간과 대기 시간의 합으로 정의하고 있다.

    Response Time = Service Time + Wait Time
    = CPU Time + Queue Time

    서비스 시간(Service Time) : 프로세스가 정상적으로 동작하며 일을 수행한 시간(CPU Time)

    대기 시간(Wait Time) : 프로세스가 잠시 수행을 멈추고 대기한 시간. ('Queue Time')

    Response Time Analysis 방법론은 Response Time을 위와 같이 정의하고, CPU Time과 Wait Time을 각각 break down 하면서 서버의 일량과 대기 시간을 분석해 나간다.

    CPU Time은 파싱 작업에 소비한 시간인지 아니면 쿼리 본연의 오퍼레이션 수행을 위해 소비한 시간인지를 분석.

    Wait Time은 각각 발생한 대기 이벤트들을 분석해 가장 시간을 많이 소비한 이벤트 중심으로 해결방안을 모색.

    Oracle 10g 기준으로 대기 이벤트 개수는 890여 개

     

    가. 라이브러리 캐시 부하

    아래는 라이브러리 캐시에서 SQL 커서를 찾고 최적화하는 과정에 경합이 발생했음을 나타나는 대기 이벤트다.

    • latch: shared pool
    • latch: library cache

    라이브러리 캐시와 관련해 자주 발생하는 대기 이벤트로는 아래 2가지가 있는데, 이들은 수행 중인 SQL이 참조하는 오브젝트에 다른 사용자가 DDL 문장을 수행할 때 나타난다.

    • library cache lock
    • library cache pin

    나. 데이터베이스 Call과 네트워크 부하

    아래 이벤트에 의해 소모된 시간은 애플리케이션과 네트워크 구간에서 소모된 시간으로 이해하면 된다.

    • SQL*Net message from client
    • SQL*Net message to client
    • SQL*Net more data to client
    • SQL*Net more data from client

    SQL*Net message from client 이벤트는 사실 데이터베이스 경합과는 관련이 없다.

    클라이언트로부터 다음 명령이 올 때까지 Idle 상태로 기다릴 때 발생하기 때문이다

    나머지 세 개의 대기 이벤트는 실제 네트워크 부하가 원인일 수 있다. SQL*Net message to client와 SQL*Net more data to client 이벤트는 클라이언트에게 메시지를 보냈는데 메시지를 잘 받았다는 신호가 정해진 시간보다 늦게 도착하는 경우에 나타나며, 클라이언트가 너무 바쁜 경우일 수도 있다. SQL*Net more data from client 이벤트는 클라이언트로부터 더 받을 데이터가 있는데 지연이 발생하는 경우다.

    다. 디스크 I/O 부하

    아래는 모두 디스크 I/O가 발생할 때마다 나타나는 대기 이벤트이다.

    • db file sequential read (single Block I/O 수행시)
    • db file scattered read (Multiblock I/O 수행시)
    • direct path read
    • direct path write
    • direct path write temp
    • direct path read temp
    • db file parallel read

    Single Block I/O : 한번의 I/O Call에 하나의 데이터 블록만 읽는 것을 말한다. 인덱스 블록을 읽을 때, 그리고 인덱스를 거쳐 테이블

    블록을 액세스할 때 이 방식을 사용.

    Multiblock I/O : I/O Call이 필요한 시점에 인접한 블록들을 같이 읽어 메모리에 적재하는 것.

    Table Full Scan 또는 Index Fast Full Scan 시 나타난다.

    라. 버퍼 캐시 경합

    아래는 버퍼 캐시에서 블록을 읽는 과정에 경합이 발생했음을 나타나는 대기 이벤트이다.

    • latch: cache buffers chains
    • latch: cache buffers lru chain
    • buffer busy waits
    • dfree buffer waits

    버퍼 캐시에서 블록을 읽더라도 이들 대기 이벤트가 심하게 발생하는 순간 동시성은 현저히 저하되는데, 이들 대기 이벤트를 해소하는 방안도 디스크 I/O 부하 해소 방안과 다르지 않다. 따라서 이들 경합의 해소 원리도 4절과 더불어 4장, 5장에서 함께 다루게 된다.

    마. Lock 관련 대기 이벤트

    아래 'enq'로 시작되는 대기 이벤트는 Lock과 관련된 것으로서, 그 발생 원인과 해소 방안을 2장에서 일부 소개한다.

    • enq: TM - contention
    • enq: TX - row lock contention
    • enq: TX - index contention
    • enq: TX - allocate ITL entry
    • enq: TX contention
    • latch free

    latch free는 특정 자원에 대한 래치를 여러 차례(2,000번 가량) 요청했지만 해당 자원이 계속 사용 중이어서 잠시 대기 상태로 빠질 때마다 발생하는 대기 이벤트다.

    Lock : 사용자 데이터를 보호하

    래치 : SGA에 공유돼 있는 갖가지 자료구조를 보호할 목적으로 사용하는 가벼운 Lock.

    래치도 일종의 Lock이지만, 큐잉(Queueing) 메커니즘을 사용하지 않는다. 따라서 특정 자원에 액세스하려는 프로세스는 래치 획득에 성공할 때까지 시도를 반복할 뿐, 우선권을 부여 받지는 못한다. 이는 가장 먼저 래치를 요구했던 프로세스가 가장 늦게 래치를 얻을 수도 있음을 뜻한다.

    지금까지 소개한 것 외에 자주 발생하는 대기 이벤트로는 아래와 같은 것들이 있다.

    • log file sync
    • checkpoint completed
    • log file switch completion
    • log buffer space
    Posted by redkite
    , |

    한개의 Oracle Database 내에 취급가능한 데이터 파일의 최대갯수는 64000개 라는군요.

    그리고 한개의 tablespace (small tablespace) 가 가질수 있는 파일의 갯수는 1022개 라고 하네요.

    (11gR2 기준)

     

    SQL> -- 현재 오라클에게 셋팅되어 있는 테이블스페이스 타입

    col property_value for a32

    select PROPERTY_NAME, PROPERTY_VALUE

    from DATABASE_PROPERTIES

    where PROPERTY_NAME = 'DEFAULT_TBS_TYPE';

     

    SQL> -- 디폴트 테이블스페이스를 변경 [BIGFILE]

    alter database set default BIGFILE tablespace;

    SQL> -- 디폴트 테이블스페이스를 변경 [SMALLFILE]

    alter database set default SMALLFILE tablespace;

    Posted by redkite
    , |

    JDBC Version JDK Driver File Name

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

    11.2.0 1.6.x ojdbc6.jar

    1.5.x ojdbc5.jar

    11.1.0 1.6.x ojdbc6.jar

    1.5.x ojdbc5.jar

    10.2.0 1.5.x ojdbc14.jar

    1.4.x ojdbc14.jar

    1.3.x classes12.jar

    1.2.x classes12.jar

    10.1.0 1.4.x ojdbc14.jar

    1.3.x classes12.jar

    1.2.x classes12.jar

    9.2.0 1.4.x ojdbc14.jar

    1.3.x classes12.zip

    1.2.x classes12.zip

    1.1.x classes111.zip

    Posted by redkite
    , |

    Oracle 11g RAC Basic Commands (운영 기본 명령어)

    #####################################################################################
    -- Listener (stop, start, status)
    -- srvctl stop listener -n <hostname> -l LISTENER
    #####################################################################################

    srvctl stop listener -n rac1 //

    -l LISTENER
    srvctl start listener -n rac1 -l LISTENER
    srvctl status listener -n rac1 -l LISTENER

    srvctl stop listener -n rac2 -l LISTENER
    srvctl start listener -n rac2 -l LISTENER
    srvctl status listener -n rac2 -l LISTENER


    #####################################################################################
    -- Oracle instance and service (status)
    -- srvctl status database -d <database_name>
    #####################################################################################

    srvctl status database -d CRMDB


    #####################################################################################
    -- Oracle 11g RAC stop commands
    -- srvctl stop instance -d <database_name> -i CRMDB1<instance_name>
    -- srvctl stop listener -n r<hostname> -l LISTENER
    -- srvctl stop nodeapps -n <hostname>
    #####################################################################################

    --RAC1

    emctl stop dbconsole

    srvctl stop instance -d CRMDB -i CRMDB1
    srvctl status instance -d CRMDB -i CRMDB1

    srvctl stop listener -n rac1 -l LISTENER
    srvctl status listener -n rac1 -l LISTENER

    srvctl stop nodeapps -n rac1

    --RAC2

    emctl stop dbconsole

    srvctl stop instance -d CRMDB -i CRMDB2
    srvctl status instance -d CRMDB -i CRMDB1

    srvctl stop listener -n rac2 -l LISTENER
    srvctl status listener -n rac2 -l LISTENER

    srvctl stop nodeapps -n rac2





    #####################################################################################
    -- Oracle 11g RAC start commands
    -- srvctl start listener -n r<hostname> -l LISTENER
    -- srvctl start instance -d <database_name> -i <instance_name>

    #####################################################################################

    --RAC1

    srvctl start nodeapps -n rac1
    srvctl status nodeapps -n rac1

    srvctl start listener -n rac1 -l LISTENER
    srvctl status listener -n rac1 -l LISTENER

    srvctl start instance -d CRMDB -i CRMDB1
    srvctl status instance -d CRMDB -i CRMDB1

    emctl start dbconsole



    --RAC2

    srvctl start nodeapps -n rac2
    srvctl status nodeapps -n rac2

    srvctl start listener -n rac2 -l LISTENER
    srvctl status listener -n rac2 -l LISTENER

    srvctl start instance -d CRMDB -i CRMDB2
    srvctl status instance -d CRMDB -i CRMDB2

    emctl start dbconsole

    #####################################################################################
    -- All Database (stop, start)
    -- srvctl start database -d <database name>
    #####################################################################################

    srvctl start database -d CRMDB
    srvctl stop database -d CRMDB

    Posted by redkite
    , |

    최근에 달린 댓글

    최근에 받은 트랙백

    글 보관함