블로그 이미지
redkite

카테고리

분류 전체보기 (291)
00.SI프로젝트 산출물 (0)
00.센터 운영 문서 (0)
01.DBMS ============.. (0)
01.오라클 (117)
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.5
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.오라클'에 해당되는 글 117건

  1. 2012.12.19 [오라클]전문가가 되기 위해 무엇을 준비해야 하는가?
  2. 2012.12.19 [오라클]데이터베이스 설계의 핵심 개념을 잡아라
  3. 2012.12.19 [오라클]전문가가 되고자 하는 사람들을 위하여
  4. 2012.12.19 [오라클]통제 관리 능력을 어떻게 키울 것인가
  5. 2012.12.19 [오라클]백업-복구 가이드 - 논리적/물리적 구조의 이해
  6. 2012.12.19 [오라클]NLS의 찰떡궁합 들여다보기 2
  7. 2012.12.19 [오라클]NLS의 찰떡궁합 들여다보기 1
  8. 2012.12.19 [오라클]유지 관리 계획
  9. 2012.12.19 [DB튜닝]개발자를 위한 튜닝 가이드 - 쿼리 디자인
  10. 2012.12.19 [오라클]성능 향상을 위한 파티션 테이블 사용은 필수!!!
  11. 2012.12.19 [오라클]log, trc 등 관리 정책 및 쉘 스크립트
  12. 2012.12.19 [오라클]SID 선택 가능 .profile(UNIX ksh)
  13. 2012.12.19 [오라클]스키마 및 오브젝트 생성 순서(스크립트 베이스)
  14. 2012.12.19 [오라클]Invalid / Disabled 된 오브젝트 관리
  15. 2012.12.19 [오라클]Alter Table 컬럼 변경 관리
  16. 2012.12.19 [오라클]Create Table As Select(CTAS)
  17. 2012.12.19 [오라클]사용자별 오브젝트 갯수 조회
  18. 2012.12.19 [오라클]LOB를 가진 테이블의 관리
  19. 2012.12.19 [오라클]파티션 인덱스 상태 변경
  20. 2012.12.19 [오라클]파티션 로컬 인덱스 생성방법
  21. 2012.12.19 [오라클]DDL-시퀀스의 LAST값 변경
  22. 2012.12.19 [오라클]Delete와 Truncate 비교
  23. 2012.12.19 [오라클]SHRINK SPACE 관리
  24. 2012.12.19 [오라클]V$session Command 컬럼의 코드표
  25. 2012.12.19 [오라클]RawDevice to File system(DD Copy)
  26. 2012.12.19 [오라클]exp/imp Ulitity 옵션
  27. 2012.12.19 [오라클]exp/imp 단순 이관 방법 및 순서
  28. 2012.12.19 [오라클]RAC 리스너 / TNS / JDBC(OCI, THIN) 설정
  29. 2012.12.19 [오라클]RAC 10gR2 spfile / 노아카이브 로그 모드 설정
  30. 2012.12.19 [오라클]RAC 10gR2 spfile / 아카이브 로그 모드 설정

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

 

저 자 : 이상원

 

필자의 지식과 능력의 한계와 짧은 지면 관계로 본 원고에서 “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
    , |

    다음의 내용들은 개인적인 권장하는 사항들이다.

    오라클 10g의 경우 엔진 설치를 위한 디렉토리 공간을 20GB 이상 주는 것을 권장한다.

    오라클의 adump, bdump, udump, 리스너 로그, 아카이브 로그 등을 백업할 수 있으면

    백업하는 것을 권장한다. 하지만 대부분의 경우 백업의 필요성이 절실하지 않기 때문에

    삭제한다.

     

    각종 로그의 자동관리를 위하여 첨부한 파일과 같이 CRONTAB에 등록하여 관리하면 편리하다.
    각각의 로그관리에 대한 정책이 필요하다.

     

    정책 예시)
    1. alert 로그       : 월별로 로그를 관리. 영구 보관하는 것이 좋다.
                          compress 명령으로 압축하여 보관.
    2. adump audit 파일 : 180일 정도 유지, 매일 180일이 지난 trc파일을 삭제
    3. bdump trace 파일 : 90일 정도 유지, 매일 90일이 지난 trc파일을 삭제
    4. udump trace 파일 : 90일 정도 유지, 매일 90일이 지난 trc파일을 삭제
    5. 리스너 로그      : 리스너를 로깅하도록 설정했을 경우 월별로 로그를 관리.

                          180일이 지난 파일은 삭제. compress 명령으로 압축하여 보관.
    6. 아카이브로그 파일 : 기본적으로 1주일에 1번 이상 FULL BACKUP을 받을 경우
       백업 툴에서 아카이브로그를 관리해 주지 않을 경우 등록하여 사용
       7일전 아카이브로그 파일 삭제.

     

    쉘 예시)

    쉘 스크립트 작성 시 오타에 주의할 것. 반드시 테스트 후 적용할 것
    #######################################################
    #### alert.log                                     ####
    #### (매월 1일 실행할 수 있도록 cron job 등록 )    ####
    #######################################################

    nDate=`date +%Y%m%d`
    cp $ORACLE_BASE/admin/TESTDB/bdump/alert_TESTDB.log $ORACLE_BASE/TESTDB/bdump/alert_TESTDB.log.$nDate
    cat /dev/null > $ORACLE_BASE/admin/TESTDB/bdump/alert_TESTDB.log
    compress -vf $ORACLE_BASE/TESTDB/bdump/alert_TESTDB.log.$nDate

     

    #######################################################
    #### listener.log                                  ####
    #### (매월 1일 실행할 수 있도록 cron job 등록 )    ####
    #######################################################

    nDate=`date +%Y%m%d`
    cp $ORACLE_HOME/network/admin/listener.log $ORACLE_HOME/network/admin/listener.log.$nDate
    cat /dev/null > $ORACLE_HOME/network/admin/listener.log
    compress -vf $ORACLE_HOME/network/admin/listener.log.$nDate

     

    #######################################################
    #### audit                                         ####

    #######################################################
    # 180일이 지난 *.aud를 찾아 삭제
    find $ORACLE_BASE/admin/TESTDB/adump \( -ctime +180 -name '*.aud' \) -exec rm -f {} \;

     

    #######################################################
    #### .trc                                          ####
    # 90일이 지난 *.trc를 찾아 삭제                    ####
    #######################################################
    find $ORACLE_BASE/admin/TESTDB/bdump \( -ctime +90 -name '*.trc' \) -exec rm -f {} \;
    find $ORACLE_BASE/admin/TESTDB/udump \( -ctime +90 -name '*.trc' \) -exec rm -f {} \;

     

    #######################################################
    #### archive log                                   ####
    #######################################################
    # 7일이 지난 *.arc를 찾아 삭제
    find /archive_log \( -ctime +7 -name '*.arc' \) -exec rm -f {} \;

     

    Posted by redkite
    , |

    1. 환경 - 오라클SID 선택 가능 .profile(UNIX,ksh) 비공개  설치 및 환경관리 / ORACLE 

    2010/08/19 15:33  수정  삭제

    복사http://geniusgx.blog.me/20112014872

    전용뷰어 보기

    1개의 서버 장비에 여러 개의 인스탄스를 띄워야 하는 경우

    다음과 같이 오라클 UNIX 계정의 .profile을 구성하면 편리하게

    관리할 수 있다.

     

    -------------------- < .profile 내용 > -----------------------------

     

    ##########################################################
    ###################### 호출 함수 #########################
    ##########################################################

    if [ "$HERE_HOME"="" ]; then
       export HERE_HOME=`echo ``pwd```
    fi

    unset ORACLE_SID

    SetSID() {
      while true
      do
        unset TEMP
        echo ""
        echo "###########################################"
        echo "#     1) 프로젝트 개발 (DEVDB)            #"
        echo "#     2) 통합     시험 (TESTDB)           #"
        echo "#     q) 종료   (default DEVDB)           #"
        echo "###########################################"
        read "TEMP? ▣ 사용할 DB를 선택하십시오 : "
        case $TEMP in
          "1")
               ORACLE_SID=DEVDB; export ORACLE_SID
               echo " [프로젝트 개발(DEVDB)]을 선택하셨습니다."
               break
               ;;
          "2")
               ORACLE_SID=TESTDB; export ORACLE_SID
               echo " [통합시험(TESTDB)]을 선택하셨습니다."  
               break
               ;;
           "Q")
               ORACLE_SID=DEVDB; export ORACLE_SID
               echo " [프로젝트 개발(DEVDB)]을 선택하셨습니다."
               break
               ;;
           "q")
               ORACLE_SID=DEVDB; export ORACLE_SID
               echo " [프로젝트 개발(DEVDB)]을 선택하셨습니다."
               break
               ;;
            *)
               unset ORACLE_SID
               ;;
         esac
      done
    }

    ##########################################################
    ###################### 함수 호출 #########################
    ##########################################################
    if [ ! "$ORACLE_SID" ]
    then
       SetSID
    fi

    ##########################################################
    ##########################################################
    ##########################################################
    export PS1='$ORACLE_SID:$PWD> '
    banner $ORACLE_SID

     

    umask 022

     

    export ORACLE_BASE=/oracle/app/oracle
    export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1
    export NLS_LANG=American_America.KO16MSWIN949
    export ORA_NLS10=$ORACLE_HOME/nls/data
    export TNS_ADMIN=$ORACLE_HOME/network/admin

     

    export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib
    export SHLIB_PATH=$ORACLE_HOME/lib32:/lib:/usr/lib

     

    export PATH=$PATH:/usr/bin:/usr/sbin:/usr/local/bin:/usr/ccs/bin
    export PATH=$PATH:/etc:/bin:/usr/ucb:/usr/bin/X11:/sbin
    export PATH=$PATH:.:$ORACLE_HOME/bin:$ORACLE_HOME/OPatch

     

    # export DISPLAY=10.182.52.156:0.0

    export LANG=C
    export TERM=vt100
    export EDITOR=vi

     

    set -o vi
    stty erase ^H

     

    alias   home='cd $ORACLE_HOME'

     

    Posted by redkite
    , |

    오라클 엔진을 설치하고나면 무엇부터 해야 할지 막막한 초보 DBA에게 도움이 되지 않을까 해서

    적어 본다.

     

    오라클10g(RAC아닌 Single), datafile이 UNIX file system의 경우를 예로 설명한다.
    Windows버전도 순서는 다르지 않다.

    데이터베이스를 설치한 후 보통의 경우 다음의 순서로 작업이 진행된다.

    좀 더 세부적인 작업이나 옵션이 필요하겠지만 단순화한 순서이므로 감안하길 바란다.


    1. 오라클 엔진 설치(INSTALL)
       1] 오라클 엔진 설치
       2] 오라클 데이터베이스 생성
       3] listener.ora 및 tnsname.ora 설정

       4] initTESTDB.ora(spfileTESTDB.ora) 설정 변경

     

    2. 스키마 및 오브젝트 생성 순서
    DB 생성이 되어 있다는 가정으로 다음과 같은 순서로

    스키마(SCHEMA) 및 OBJECT를 생성한다면 무난할 것이다.

       1] 데이터파일을 저장할 Directory를 생성 
          % su - oracle
          % mkdir -p /data01/TESTDB
          % mkdir -p /data02/TESTDB

     

     

       2] 테이블스페이스(TABLESPACE) 생성
          SQL> CONNECT / as sysdba
          -- 데이터용 테이블스페이스 생성
          SQL> CREATE TABLESPACE       TS_GDXX01
                      datafile       '/data01/TESTDB/TS_GDXX01_01.dbf' size 50M REUSE
                      extent management local  autoallocate
                      segment space management auto;

          -- 인덱스용 테이블스페이스 생성
          SQL> CREATE TABLESPACE       TS_GIXX01
                      datafile       '/data02/TESTDB/TS_GIXX01_01.dbf' size 50M REUSE
                      extent management local  autoallocate
                      segment space management auto;

     

       3] 사용자(USER) 생성 
          SQL> CONNECT / as sysdba
          -- 응용프로그램 접속 계정(최소권한) : US_APPL
          SQL> CREATE USER                 US_APPL
                      IDENTIFIED BY        password
                      DEFAULT TABLESPACE   TS_GDXX01
                      TEMPORARY TABLESPACE TEMP;
          SQL> GRANT ALTER SESSION, CREATE SESSION         TO US_APPL;
          SQL> GRANT CREATE SYNONYM, UNLIMITED TABLESPACE  TO US_APPL;

          -- 오브젝트 소유 계정 : US_OWNER
          SQL> CREATE USER                 US_OWNER
                      IDENTIFIED BY        password
                      DEFAULT TABLESPACE   TS_GDXX01
                      TEMPORARY TABLESPACE TEMP;

          SQL> GRANT CONNECT, RESOURCE                            TO US_OWNER;
          SQL> GRANT CREATE PUBLIC SYNONYM, UNLIMITED TABLESPACE  TO US_OWNER;
       
       4] 테이블(TABLE) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE TABLE TB_GXX001  (
                      PgmID     VARCHAR2(30) NOT NULL,
                      PgmNm     VARCHAR2(50) NULL,
                      BatchType CHAR(1) NULL
                      ChngDtime VARCHAR2(14) NOT NULL,
                      CONSTRAINT IX_GXX001_PK  PRIMARY KEY (PgmID)
                                 USING INDEX TABLESPACE TS_GIXX01
                     ) TABLESPACE TS_GDXX01;
       
       5] 인덱스(INDEX) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE INDEX IX_GXX001 ON TB_GXX001
                      ( BatchType ASC, PrgNm ASC )
                      TABLESPACE TS_GIXX01;
       
       6] 테이블(TABLE) 권한부여(GRANT) 
          SQL> CONNECT US_OWNER/password
          SQL> GRANT INSERT, SELECT, UPDATE, DELETE ON TB_GXX001  TO US_APPL;
       
       7] 테이블(TABLE) 동의어(SYNONYM) 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE PUBLIC SYNONYM TB_GXX001  FOR US_OWNER.TB_GXX001;
       
       8] 시퀀스(SEQUENCE) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE SEQUENCE SQ_GXX001  START WITH 1 INCREMENT BY 1 MAXVALUE 999999 CYCLE ORDER CACHE 10;
          SQL> GRANT SELECT ON SQ_GXX001  TO US_OWNER;
          SQL> CREATE PUBLIC SYNONYM SQ_GXX001  FOR US_OWNER.SQ_GXX001 ;
       
       9] 뷰(VIEW) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE VIEW VW_GXX001_LIST .... ;
          SQL> GRANT SELECT ON VW_GXX001_LIST     TO US_APPL;
          SQL> CREATE PUBLIC SYNONYM VW_GXX001_LIST FOR US_OWNER.VW_GXX001_LIST;
       
       10] 저장 프로시져(STORED PROCEDURE/FUNCTION 등) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE OR REPLACE PROCEDURE SP_GXX001_LIST ....;
          SQL> CREATE OR REPLACE FUNCTION  SF_GXX001_LIST ....;
          SQL> GRANT EXCUTE ON SP_GXX001_LIST     TO US_APPL;
          SQL> GRANT EXCUTE ON SF_GXX001_LIST     TO US_APPL;
          SQL> CREATE PUBLIC SYNONYM SP_GXX001_LIST FOR US_OWNER.SP_GXX001_LIST;
          SQL> CREATE PUBLIC SYNONYM SF_GXX001_LIST FOR US_OWNER.SF_GXX001_LIST;
       
       11] 외래키(FK CONSTRAINT) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> ALTER TABLE TB_GXX001  ADD ( CONSTRAINT FK__GXX001
                     FOREIGN KEY ( SubPgmID ) REFERENCES TB_GXX999 ( PgmID ) );
       
       12] 트리거(TRIGGER) 생성 
          SQL> CONNECT US_OWNER/password
          SQL> CREATE OR REPLACE TRIGGER TR_GXX001_LIST .... ;


       13] 기타 필요한 것들 생성 및 등록

             - DB LINK, M-VIEW, DBMS JOB 등록 등

    Posted by redkite
    , |

    오라클의 OBJECT들 중 INVALID나 DISABLED된 것을 체크하는 스크립트이다.

    SET PAGESIZE 1000 LINESIZE 120
    COLUMN OBJECT_DV   FORMAT A10
    COLUMN OWNER       FORMAT A15
    COLUMN TABLE_NAME  FORMAT A30
    COLUMN OBJECT_TYPE FORMAT A20
    COLUMN NAME        FORMAT A30
    COLUMN STATUS      FORMAT A10

     

    SELECT * FROM
    (
    SELECT 'CONSTRAINT'    OBJECT_DV        ,
           OWNER                            ,
           TABLE_NAME      OBJECT_NAME      ,
           CONSTRAINT_TYPE OBJECT_TYPE      ,
           CONSTRAINT_NAME NAME             ,
           STATUS
      FROM SYS.DBA_CONSTRAINTS
     WHERE STATUS = 'DISABLED'
    UNION ALL
    SELECT 'TRIGGER'    OBJECT_DV           ,
           OWNER                            ,
           TABLE_NAME   OBJECT_NAME         ,
           'TRIGGER'    OBJECT_TYPE         ,
           TRIGGER_NAME NAME                ,
           STATUS
      FROM SYS.DBA_TRIGGERS
     WHERE STATUS = 'DISABLED'
    UNION ALL
    SELECT 'OBJECT'    OBJECT_DV            ,
           OWNER                            ,
           'OBJECT'    OBJECT_NAME          ,
           OBJECT_TYPE OBJECT_TYPE          ,
           OBJECT_NAME NAME                 ,
           STATUS
      FROM SYS.DBA_OBJECTS
     WHERE STATUS      = 'INVALID'
    UNION ALL
    SELECT 'INDEX'           OBJECT_DV      ,
           OWNER             OWNER          ,
           INDEX_NAME        OBJECT_NAME    ,
           'INDEX'           OBJECT_TYPE    ,
           INDEX_NAME        NAME           ,
           STATUS
      FROM SYS.DBA_INDEXES
     WHERE STATUS IN ('UNUSABLE','INVALID')
    UNION ALL
    SELECT 'PART IDX'        OBJECT_DV      ,
           INDEX_OWNER       OWNER          ,
           INDEX_NAME        OBJECT_NAME    ,
           'PART INDEX'      OBJECT_TYPE    ,
           PARTITION_NAME    NAME           ,
           STATUS
      FROM SYS.DBA_IND_PARTITIONS
     WHERE STATUS IN ('UNUSABLE','INVALID')
    )
     WHERE OWNER NOT IN ('SYS','SYSTEM');

     

    Posted by redkite
    , |

    1. 컬럼 추가
    SQL> ALTER TABLE 테이블
                       ADD (컬럼1 VARCHAR2(03)     NULL,
                            컬럼2 NUMBER(05)       NULL);

     

    2. 컬럼TYPE 및 LENGTH 변경
    SQL> ALTER TABLE 테이블
                        MODIFY (컬럼1 VARCHAR2(05)      NULL,
                                컬럼2 NUMBER(08)    NOT NULL);

     

    3. 컬럼 DEFAULT 변경
    SQL> ALTER TABLE 테이블
               MODIFY (컬럼1 VARCHAR2(05)  DEFAULT 'N' NOT NULL,
                       컬럼2 NUMBER(08)    DEFAULT 0   NOT NULL,

                       컬럼3 DATE          DEFAULT SYSDATE NOT NULL);

    4. 컬럼 삭제(8i~)
    SQL> ALTER TABLE 테이블 DROP COLUMN 컬럼1;    -- 1개 컬럼
    SQL> ALTER TABLE 테이블 DROP (컬럼1, 컬럼2);  -- 2개 컬럼 이상

     

    5. 사용하지 않는 컬럼으로 표시(8i~)

    SQL> ALTER TABLE 테이블 SET UNUSED COLUMN 컬럼1;   -- 1개 컬럼
    SQL> ALTER TABLE 테이블 SET UNUSED (컬럼1, 컬럼2); -- 2개 컬럼 이상

     

    6. 사용하지 않는 column으로 표시된 컬럼 삭제(8i~)
    SQL> ALTER TABLE 테이블 DROP UNUSED COLUMNS CHECKPOINT 1000;

     

    7. 컬럼 RENAME 방법
       - ~ 8i          : TABLE 재생성
       - 9iR1(9.0.1) ~ : DBMS_REDEFINITION을 이용(Bulletin No: 12279 참조)
       - 9iR2(9.2.0) ~ : SQL> ALTER TABLE 테이블 RENAME COLUMN old_컬럼 TO new_컬럼;

     

    8. 여러 테이블에서 사용하는 컬럼의 길이를 변경하고자 할 경우

    SQL> SELECT  'ALTER TABLE '||TABLE_NAME||' MODIFY ('||COLUMN_NAME||' '||DATA_TYPE||'(7));'
           FROM  USER_TAB_COLUMNS
          WHERE  COLUMN_NAME LIKE '%컬럼%';

    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
    , |
    SELECT
            A.OWNER,
            A.TABLE_COUNT,
            A.VIEW_COUNT,
            B.PK_COUNT,
            A.INDEX_COUNT,
            B.UK_COUNT,
            B.FK_COUNT,
            B.CK_COUNT,
            A.SYNONYM_COUNT,
            A.SEQUENCE_COUNT,
            A.FUNCTION_COUNT,
            A.PROCEDURE_COUNT,
            A.PACKAGE_COUNT,  
            A.PACKAGE_BODY_COUNT,
            A.TRIGGER_COUNT,
            A.LOB_COUNT,
            A.TABLE_PARTITION_COUNT,
            A.INDEX_PARTITION_COUNT,
            A.CLUSTER_COUNT,   
            A.LIBRARY_COUNT,   
            A.DIRECTORY_COUNT,   
            A.JAVA_SOURCE_COUNT,   
            A.JAVA_CLASS_COUNT,
            A.JAVA_RESOURCE_COUNT,
            A.JAVA_DATA_COUNT,
            A.INDEXTYPE_COUNT,
            A.OPERATOR_COUNT,
            A.TYPE_COUNT,
            A.TYPE_BODY_COUNT,
            A.RESOURCE_PLAN_COUNT,
            A.CONSUMER_GROUP_COUNT,
            A.MATERIALIZED_VIEW_COUNT,
            A.TABLE_SUBPARTITION_COUNT,
            A.INDEX_SUBPARTITION_COUNT,
            A.LOB_PARTITION_COUNT,
            A.LOB_SUBPARTITION_COUNT,
            A.DIMENSION_COUNT,
            A.CONTEXT_COUNT,  
            A.RULE_SET_COUNT,  
            A.XML_SCHEMA_COUNT,
            A.SECURITY_PROFILE_COUNT,
            A.RULE_COUNT,
            A.CAPTURE_COUNT,
            A.APPLY_COUNT,
            A.EVALUATION_CONTEXT_COUNT,
            A.PROGRAM_COUNT,
            A.JOB_COUNT,
            A.JOB_CLASS_COUNT,
            A.SCHEDULE_COUNT,
            A.WINDOW_COUNT,
            A.WINDOW_GROUP_COUNT,
            A.CHAIN_COUNT,
            A.NEXT_OBJECT_COUNT,
            A.QUEUE_COUNT,
            A.FILE_GROUP_COUNT
     FROM
     (SELECT /*+ ALL_ROWS */
             U.NAME                      AS OWNER,
             SUM(DECODE(O.TYPE#,  0, 1)) AS NEXT_OBJECT_COUNT,
             SUM(DECODE(O.TYPE#,  1, 1)) AS INDEX_COUNT,
             SUM(DECODE(O.TYPE#,  2, 1)) AS TABLE_COUNT,
             SUM(DECODE(O.TYPE#,  3, 1)) AS CLUSTER_COUNT,
             SUM(DECODE(O.TYPE#,  4, 1)) AS VIEW_COUNT,
             SUM(DECODE(O.TYPE#,  5, 1)) AS SYNONYM_COUNT,
             SUM(DECODE(O.TYPE#,  6, 1)) AS SEQUENCE_COUNT,
             SUM(DECODE(O.TYPE#,  7, 1)) AS PROCEDURE_COUNT,
             SUM(DECODE(O.TYPE#,  8, 1)) AS FUNCTION_COUNT,
             SUM(DECODE(O.TYPE#,  9, 1)) AS PACKAGE_COUNT, 
             SUM(DECODE(O.TYPE#, 11, 1)) AS PACKAGE_BODY_COUNT,
             SUM(DECODE(O.TYPE#, 12, 1)) AS TRIGGER_COUNT,
             SUM(DECODE(O.TYPE#, 13, 1)) AS TYPE_COUNT,
             SUM(DECODE(O.TYPE#, 14, 1)) AS TYPE_BODY_COUNT,
             SUM(DECODE(O.TYPE#, 19, 1)) AS TABLE_PARTITION_COUNT,
             SUM(DECODE(O.TYPE#, 20, 1)) AS INDEX_PARTITION_COUNT,
             SUM(DECODE(O.TYPE#, 21, 1)) AS LOB_COUNT,
             SUM(DECODE(O.TYPE#, 22, 1)) AS LIBRARY_COUNT,
             SUM(DECODE(O.TYPE#, 23, 1)) AS DIRECTORY_COUNT,
             SUM(DECODE(O.TYPE#, 24, 1)) AS QUEUE_COUNT,
             SUM(DECODE(O.TYPE#, 28, 1)) AS JAVA_SOURCE_COUNT,
             SUM(DECODE(O.TYPE#, 29, 1)) AS JAVA_CLASS_COUNT,
             SUM(DECODE(O.TYPE#, 30, 1)) AS JAVA_RESOURCE_COUNT,
             SUM(DECODE(O.TYPE#, 32, 1)) AS INDEXTYPE_COUNT,
             SUM(DECODE(O.TYPE#, 33, 1)) AS OPERATOR_COUNT,
             SUM(DECODE(O.TYPE#, 34, 1)) AS TABLE_SUBPARTITION_COUNT,
             SUM(DECODE(O.TYPE#, 35, 1)) AS INDEX_SUBPARTITION_COUNT,
             SUM(DECODE(O.TYPE#, 40, 1)) AS LOB_PARTITION_COUNT,
             SUM(DECODE(O.TYPE#, 41, 1)) AS LOB_SUBPARTITION_COUNT,
             SUM(DECODE(O.TYPE#, 42, NVL((SELECT DISTINCT 'REWRITE EQUIVALENCE'
                                            FROM SYS.SUM$ S
                                           WHERE S.OBJ#=O.OBJ#
                                             AND bitand(S.XPFLAGS, 8388608) = 8388608),
                                          1))) AS MATERIALIZED_VIEW_COUNT,
             SUM(DECODE(O.TYPE#, 43, 1)) AS DIMENSION_COUNT,
             SUM(DECODE(O.TYPE#, 44, 1)) AS CONTEXT_COUNT,
             SUM(DECODE(O.TYPE#, 46, 1)) AS RULE_SET_COUNT,
             SUM(DECODE(O.TYPE#, 47, 1)) AS RESOURCE_PLAN_COUNT,
             SUM(DECODE(O.TYPE#, 48, 1)) AS CONSUMER_GROUP_COUNT,
             SUM(DECODE(O.TYPE#, 55, 1)) AS XML_SCHEMA_COUNT,
             SUM(DECODE(O.TYPE#, 56, 1)) AS JAVA_DATA_COUNT,
             SUM(DECODE(O.TYPE#, 57, 1)) AS SECURITY_PROFILE_COUNT, 
             SUM(DECODE(O.TYPE#, 59, 1)) AS RULE_COUNT, 
             SUM(DECODE(O.TYPE#, 60, 1)) AS CAPTURE_COUNT, 
             SUM(DECODE(O.TYPE#, 61, 1)) AS APPLY_COUNT,
             SUM(DECODE(O.TYPE#, 62, 1)) AS EVALUATION_CONTEXT_COUNT,
             SUM(DECODE(O.TYPE#, 66, 1)) AS JOB_COUNT,
             SUM(DECODE(O.TYPE#, 67, 1)) AS PROGRAM_COUNT,
             SUM(DECODE(O.TYPE#, 68, 1)) AS JOB_CLASS_COUNT,
             SUM(DECODE(O.TYPE#, 69, 1)) AS WINDOW_COUNT,
             SUM(DECODE(O.TYPE#, 72, 1)) AS WINDOW_GROUP_COUNT,
             SUM(DECODE(O.TYPE#, 74, 1)) AS SCHEDULE_COUNT,
             SUM(DECODE(O.TYPE#, 79, 1)) AS CHAIN_COUNT,
             SUM(DECODE(O.TYPE#, 81, 1)) AS FILE_GROUP_COUNT
        FROM SYS.OBJ$ O, SYS.USER$ U
       WHERE O.OWNER# = U.USER# 
         AND U.NAME NOT IN ('SYS','SYSTEM','BIZMAX','DBSNMP','OUTLN','WMSYS','ORDSYS','TSMSYS',
                            'ORDPLUGINS','RMAN','MDSYS','CTXSYS','WKSYS','WKPROXY','ODM',
                            'ODM_MTR','OLAPSYS','PERFSTAT','ORANGE','WASJMS','SYSMAN',
                            'TMAX','DBMSTOOL','MIBWINE' )
       GROUP BY U.NAME ) A,
      (SELECT /*+ ALL_ROWS */
             OWNER                       AS OWNER,
             SUM(DECODE(C.CONSTRAINT_TYPE, 'P', 1)) AS PK_COUNT,
             SUM(DECODE(C.CONSTRAINT_TYPE, 'U', 1)) AS UK_COUNT,
             SUM(DECODE(C.CONSTRAINT_TYPE, 'R', 1)) AS FK_COUNT,
             SUM(DECODE(C.CONSTRAINT_TYPE, 'C', 1)) AS CK_COUNT 
        FROM SYS.DBA_CONSTRAINTS C
       WHERE C.OWNER NOT IN ('SYS','SYSTEM','BIZMAX','DBSNMP','OUTLN','WMSYS','ORDSYS','TSMSYS',
                             'ORDPLUGINS','RMAN','MDSYS','CTXSYS','WKSYS','WKPROXY','ODM',
                             'ODM_MTR','OLAPSYS','PERFSTAT','ORANGE','WASJMS','SYSMAN',
                             'TMAX','DBMSTOOL','MIBWINE' )
         AND C.GENERATED = 'USER NAME'
       GROUP BY C.OWNER ) B
       WHERE A.OWNER = B.OWNER(+);

     

    Posted by redkite
    , |

    LOB 컬럼을 가진 테이블에 대한 명령어를 정리해 보았다.

    1. LOB 컬럼을 포함한 테이블 생성
       -----------------------------
       SQL> CREATE TABLE 테이블명 (
                   일반컬럼명    VARCHAR2(255) NOT NULL,
                   LOB컬럼명     CLOB NULL,
                   CONSTRAINT PK인덱스명 PRIMARY KEY (일반컬럼명)
                   USING INDEX TABLESPACE 인덱스테이블스페이스명
            )
            TABLESPACE 데이터테이블스페이스명
            LOB(LOB컬럼명) STORE AS LD_테이블명_CLOB
               (TABLESPACE LOB데이터테이블스페이스명 DISABLE STORAGE IN ROW
                     INDEX LI_테이블명_CLOB (TABLESPACE LOB인덱스테이블스페이스명))
            ;

    2. LOB 컬럼 추가
       -------------
       SQL> alter table 테이블명  add (컬럼명 BLOB)
                  lob(컬럼명) STORE AS LD_테이블명_BLOB (TABLESPACE 데이터LOB테이블스페이스명
                              INDEX LI_테이블명_BLOB (TABLESPACE 인덱스LOB테이블스페이스명));

    3. LOB MOVE
       -----------
       - 인덱스 REBUILD 필요
       SQL> alter table 테이블명 move lob(컬럼명) store as (tablespace 테이블스페이스명);
       SQL> alter table 테이블명 move lob(컬럼명) STORE AS LD_테이블명_BLOB

                  (TABLESPACE 데이터LOB테이블스페이스명
                   INDEX LI_테이블명_BLOB (TABLESPACE 인덱스LOB테이블스페이스명));
       SQL> alter index 인덱스명 rebuild tablespace 테이블스페이스명

                  nologging parallel(degree 8);
       SQL> alter index 인덱스명 logging noparallel;

    4. LOB RENAME
       ----------
       - LOB DATA는  Rename됨
       - LOB INDEX는 Rename되지 않음
       - 인덱스 rebuild 필요
       SQL> alter table 테이블명 move lob(컬럼명) store as (tablespace 테이블스페이스명);
       SQL> alter table 테이블명 move lob(컬럼명) STORE AS LD_테이블명_BLOB

                  (TABLESPACE 데이터LOB테이블스페이스명
                   INDEX LI_테이블명_BLOB (TABLESPACE 인덱스LOB테이블스페이스명));
       SQL> alter index 인덱스명 rebuild tablespace 테이블스페이스명

                  nologging parallel(degree 8);
       SQL> alter index 인덱스명 logging noparallel;

    5. LOB 을 포함한 파티션 테이블 생성
       --------------------------------
       - LOB컬럼을 파티션키로 사용할 수 없다.

       SQL> CREATE TABLE 테이블명
            (
                   일반컬럼명    VARCHAR2(8) NOT NULL,
                   LOB컬럼명     BLOB NULL,
            )
            PARTITION BY RANGE(일반컬럼명)
            (
              PARTITION PT_테이블명_200803 VALUES LESS THAN ('20080399')
                        TABLESPACE 데이터테이블스페이스명
                        LOB(LOB컬럼명) STORE AS LD_PT_테이블명_200803_BLOB
                           (TABLESPACE LOB데이터테이블스페이스명 DISABLE STORAGE IN ROW
                            INDEX LI_PT_테이블명_200803_BLOB

                                  (TABLESPACE LOB인덱스테이블스페이스명)),
              PARTITION PT_테이블명_200804 VALUES LESS THAN ('20080499')
                        TABLESPACE 데이터테이블스페이스명
                        LOB(LOB컬럼명) STORE AS LD_PT_테이블명_200804_BLOB
                           (TABLESPACE LOB데이터테이블스페이스명 DISABLE STORAGE IN ROW
                            INDEX LI_PT_테이블명_200804_BLOB

                                  (TABLESPACE LOB인덱스테이블스페이스명)),
              PARTITION PT_테이블명_999999 VALUES LESS THAN ('99999999')
                        TABLESPACE 데이터테이블스페이스명
                        LOB(LOB컬럼명) STORE AS LD_PT_테이블명_999999_BLOB
                           (TABLESPACE LOB데이터테이블스페이스명 DISABLE STORAGE IN ROW
                            INDEX LI_PT_테이블명_999999_BLOB

                                  (TABLESPACE LOB인덱스테이블스페이스명))
            );

    6. LOB 을 포함한 파티션 테이블 SPLIT
       ---------------------------------
       - 인덱스 rebuild 필요
       SQL> ALTER TABLE 테이블명 SPLIT PARTITION PT_테이블명_999999 AT ('20080599')
            INTO (PARTITION PT_테이블명_200805 TABLESPACE TS_GPDRM06 
                        LOB(LOB컬럼명) STORE AS LD_PT_테이블명_200806_BLOB
                           (TABLESPACE 데이터LOB테이블스페이스명 DISABLE STORAGE IN ROW
                            INDEX LI_PT_테이블명_200806_BLOB

                                  (TABLESPACE 인덱스LOB테이블스페이스명)),
                  PARTITION PT_테이블명_999999 TABLESPACE 데이터테이블스페이스명
                        LOB(LOB컬럼명) STORE AS LD_PT_테이블명_999999_BLOB
                           (TABLESPACE 데이터LOB테이블스페이스명 DISABLE STORAGE IN ROW
                            INDEX LI_PT_테이블명_999999_BLOB

                                  (TABLESPACE 인덱스LOB테이블스페이스명))
            );

    7. LOB SEGMENT 조회
       SQL> select * from dba_lobs;
       SQL> select * from dba_segments;

    참고) 오라클10g의 경우 테이블 purge옵션을 주지 않고 drop했을 경우
             LOB세그먼트가 남아 있어 같은 LOB세그먼트로 생성시 동일 객체
             이름이 존재한다는 에러 발생.
             drop table 테이블명 purge; 또는 purge recyclebin;으로 삭제 후 생성.

     

    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
    , |

    이관 및 시스템을 구축하다보면

    테이블의 컬럼에 시퀀스를 사용하였는데 테이블에 들어있는 MAX값이

    시퀀스의 Last값과 차이가 있을 때 중복이 발생하거나 시퀀스의 Last값을 줄여 놓아야

    할 필요가 있을 때가 있다. 다음과 같이 사용하면 시퀀스를 Drop하지 않고도 시퀀스의

    Last값을 조정할 수 있다.(Alter Sequence 명령이 있다면 좋을텐데...)

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

    1. 시퀀스Last값과 컬럼MAX값의 갭이 크지 않으면서 시퀀스Last값을 증가시킬

        필요가 있을 경우 다음과 같이 nextval을 증가시킨다.
        SQL> select 시퀀스명.nextval

                    from dual connect by level <= (12514  -       10001);
                                                               -----          -----
                                                            컬럼MAX값    시퀀스Last값


    2. 시퀀스Last값과 컬럼MAX값의 갭이 크다면 increment값으로 증가/감소 시키기

    2-1. 시퀀스 정보 조회
          SQL> select * from user_sequences

                   where sequence_name ='시퀀스명';

     

    2-2. 차이값 구하기
         SQL> select 컬럼MAX값 - 시퀀스Last값 from dual;

         -- 결과

         1671677-169
         -----------
                -19882

     

    3. 차이값만큼 increment를 setting
       SQL> alter sequence 시퀀스명 increment by -19882;

     

    4. increment를 setting한 만큼의 값으로 Nextval을 이용하여 증가시킴
       SQL> select 시퀀스명.nextval from dual;

     

    5. increment를 다시 원래의 증가값으로 setting
       SQL> alter sequence 시퀀스명 increment by 1;

     

    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
    , |

    SHRINK SPACE는 오라클10g부터 지원하는 기능으로,

    세그먼트의 데이터 조각모음(ONLINE Reorg. 효과) 기능.

     

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

    1. SHRINK 작업을 수행하기 위한 주요 환경 및 주의할 점
    -----------------------------------------------------

       1) 오라클 10g 이상(Init.ora parameter 'Compatible' must be >=10.0)
       2) 세그먼트 관리방식이 반드시 ASSM(Auto Segment Space Managed) Tablespace이어야 함
       3) 약 20여건씩 INSERT/DELETE하고 COMMIT하는 방식으로 SHRINK함
       4) 개별 ROW 또는 데이터 BLOCK에 대한 LOCK(ENQUEUE)이 사용
       5) FBI(Function-Based Index)를 SHRINK하는 경우
          오류 발생하므로 FBI를 DROP한 다음 작업하고
          SHRINK작업이 완료된 후 다시 생성
       6) DML TRIGGER를 발생시키지 않음(ROWID based TRIGGER는 작업 전에 DISABLED 필요)
       7) DML 작업은 세그먼트 SHRINK 중 수행 가능하나, parallel DML을 수행될 수 없음 
       8) 세그먼트를 SHRINK 시키는 특정 단계(HWM을 조정하는 단계)에서 세그먼트에

          exclusive 모드로 짧은 시간동안 LOCK(TM)이 걸림

     

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

    2. SHRINK 작업 방법 (순서 1]~6])
    -----------------------------------------------------

       1] SHRINK 대상 테이블 분석 및 조회
          (NUM_ROWS에 비해 사용하는 BLOCKS가 과다하면 SHRINK필요)
          SQL> connect / as sysdba

          SQL> EXECUTE DBMS_UTILITY.ANALYZE_SCHEMA('소유자', 'COMPUTE');

          SQL> SELECT OWNER,     TABLE_NAME,   NUM_ROWS,
                      BLOCKS,    EMPTY_BLOCKS, AVG_SPACE,
                      CHAIN_CNT, AVG_ROW_LEN
                 FROM DBA_TABLES
                WHERE OWNER = '소유자'
                  AND EMPTY_BLOCKS / (BLOCKS + EMPTY_BLOCKS) < 0.1
                  AND (BLOCKS + EMPTY_BLOCKS) > 0
                ORDER BY BLOCKS DESC;

     

          [참고] DBMS_SPACE.VERIFY_SHRINK_CANDIDATE

     

       2] 대상 테이블 및 관련 인덱스 NOLOGGING 설정

          SQL> connect 소유자/암호
          SQL> ALTER TABLE 테이블명 NOLOGGING;
          SQL> ALTER TABLE 인덱스명 NOLOGGING;

     

       3] 대상 테이블의 ROW MOVEMENT 기능 활성화
          (데이터의 조각모음으로 ROWID가 변경되므로 ENABLE ROW MOVEMENT)
          SQL> ALTER TABLE 테이블명 ENABLE ROW MOVEMENT;

     

       4] 필요한 작업을 선택적으로 작업
          4-1) 테이블만 SHRINK하고 HWM(High Water Mark)는 SHRINK하지 않음
               SQL> ALTER TABLE 테이블명 SHRINK SPACE COMPACT;

     

          4-2) 테이블과 HWM(High Water Mark)를 SHRINK
               (HWM SHRINK 시 TM 락 발생)
               SQL> ALTER TABLE 테이블명 SHRINK SPACE;

     

          4-3) 테이블과 테이블의 HWM을 SHRINK
               SQL> ALTER TABLE 테이블명 SHRINK SPACE COMPACT;
               SQL> ALTER TABLE 테이블명 SHRINK SPACE;

     

          4-4) 인덱스와 인덱스의 HWM을 SHRINK
               (ROWID가 변경되지 않으므로 ENABLE ROW MOVEMENT 불필요)
               SQL> ALTER INDEX 인덱스명 SHRINK SPACE COMPACT;
               SQL> ALTER INDEX 인덱스명 SHRINK SPACE;

     

          4-5) 테이블 및 관련된 인덱스를 모두 SHRINK
               SQL> ALTER TABLE 테이블명 SHRINK SPACE CASCADE COMPACT;
               SQL> ALTER TABLE 테이블명 SHRINK SPACE CASCADE;

     

       5] Row movement 비활성화
          SQL> ALTER TABLE 테이블명 DISABLE ROW MOVEMENT;

     

       6] 대상 테이블 및 관련 인덱스 LOGGING 설정
          SQL> ALTER TABLE 테이블명 LOGGING;
          SQL> ALTER TABLE 인덱스명 LOGGING;

     

       ※ M-View 형태의 테이블을 SHRINK
          on-commit materialized view와 연관된 세그먼트는 SHRINK 시킬 수 없음
          rowid에 기반을 둔 materialized view에 대해서는 refresh 또는 rebuild를 수행 필요
          SQL> ALTER TABLE M-View명 SHRINK SPACE COMPACT;
          SQL> ALTER TABLE M-View명 SHRINK SPACE;

    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
    , |
    1. raw device에는 LVCB(Logical Volume Control Block)가 있지만
       file system에는 없음.

     

       - bs    : 파일 입출력의 block(버퍼) 크기

       - skip  : 입력 파일에서 처리하지 않고 통과할 블록의 개수
                 (Raw Device to Filesystem 복사 시 지정해야 함)
       - seek  : 출력 파일에서 처리하지 않고 통과할 블록의 개수
                 (Filesystem to Raw Device 복사 시 지정해야 함)

       - count : 복사할 회수 or 블록의 개수 (생략 시 모든 데이터 복사 )

                 (Raw Device to Filesystem 복사 시 반드시 명시해야 함,
                  그 이외의 경우는 생략 가능)

    플랫폼
    LVCB
    플랫폼
    LVCB
    Solaris
    0
    True64
    64KB
    HP-UX
    0
    Linux
    0
    AIX
    4KB
    Windows
    0

     

    2. dbfsize로 확인

       $ORACLE_HOME/bin/dbfsize <Oracle Datafile 명>

       [file system 결과]
       /data05/TESTDB] dbfsize UNDO01_01.dbf
       Database file: UNDO01_01.dbf
       Database file type: file system             : File Type
       Database file size: 128000 8192 byte blocks :8192 byte Block이 128000 개

     

       [raw device 결과]

       Database file type: raw device             :

    File Type
       Database file size: 1408 8192 byte blocks  : 8192 byte Block이 1408 개

       ※ dbsize로 조회한 결과(Dictionary View에서 select로 조회한
          block 수도 마찬가지)에는 Datafile Header Block 및 LVCB가 포함되지 않음
     
          다음과 같은 경우에는 파일이 손상된 경우이므로 다시 복사
          Header block file size is bad;            trying raw file format...
          Header block magic number is bad
     
    3. 참고사항
    1) Raw Device 에서 Filesystem으로 변환
       dd if=/dev/rv_data001 of=/data01/TESTDB/data001.dbf bs=4096
          skip=1 count=2818
    2) Filesystem 에서 Raw Device로 변환
       dd if=/data01/TESTDB/data001.dbf of=/dev/rv_data001 bs=4096 seek=1
    3) Raw Device 에서 Raw Device로 복사
       dd if=/dev/re_data001 of=/dev/rv_data001_bk bs=4096 skip=1 seek=1
    4) Filesystem 에서 file system으로 복사
       cp /data01/TESTDB/data001.dbf /data01/TESTDB/data001.bak
    Posted by redkite
    , |
    1. export / import
      가. 여러개의 테이블 중에서 특정 table만 백업/복구 하고자 할 때
      나. 오라클의 버전, 플랫폼이 서로 다른 상황에서의 서버간 데이터 이동 시(migration)

    2. export 방식
      가. Conventional Path export : Evaluation Buffer를 사용하는 방식, DB Buffer cache에서 필요데이터를 Evaluation Buffer로 복사 후 데이터를 가공(text -> binary)하여 디스크에 파일로 저장함. export 작업 중에 발생하는 DDL, DML 등의 명령들은 백업파일에 반영되지 않는다.(백업 파일은 Evaluation Buffer을 이용하여 작업하기 때문)

      나. Dircet Path export : DB Buffer Cache에서 데이터를 가공(text -> Binary)하여 디스크에 파일로 저장함, export 명령 이후에 백업대상이 되는 테이블스페이스나 테이블에 Lock이 발생하기 때문에 DDL, DML 작업은 실패 또는 보류 된다.

    사용자 삽입 이미지사용자 삽입 이미지



    3. export 옵션 및 사용예제
      가. 옵션
        - userid/passwd : export를 수행하는 계정/패스워드
        - buffer : Evaluation Buffer크기 지정(용량이 클 수록 export 작업이 빨라진다)
        - file : export 결과를 저장할 파일명
        - full : 전체 DB를 export 할 것인가 지정
        - owner : export 받을 사용자 이름지정
        - tables : export 받을 테이블 이름 지정
        - tablespaces : exprot 받을 테이블스페이스 이름지정
        - parfile : export 옵션을 미리 지정한 파라미터 파일지정

      나. 사용예제
    exp system/oracle full=y file=/backup/export/test01.dmp

    exp system/oracle full=y file=/backup/export/test02.dmp direct=y

    exp system/oracle tables=emp \
    file=('/backup/export/test03_1.dmp', '/backup/export/test03_2.dmp') filesize=10M

    exp system/oracle tablespaces=(example, undotbs1) file=/backup/export/test04.dmp

    exp system/oracle file=/backup/export/test05.dmp owner=(scott, hr)

    exp system/oracle file=/backup/export/test06.dmp full=y buffer=1024000

    vi par_full.dat
    file=/backup/export/test07.dmp
    full=y
    dircet=y

    exp system/oracle parfile=par_full.dat

    exp scott/tiger query=\"where ename like \'F%\'\" tables=emp \
    file=/backup/export/test07.dmp


    4. import 옵션 및 사용예제
      가. 옵션(export의 옵션과 유사하다)
        - userid/passwd : import를 수행하는 계정/패스워드
        - buffer : Evaluation Buffer크기 지정(용량이 클 수록 import 작업이 빨라진다)
        - full : export  파일의 모든 데이터를 import 한다.
        - file : import 할 export 파일명 지정
        - show : 데이터를 import 하지 않고 내용만 확인함
        - ignore : import 작업 중 발생할 수 있는 에러를 무시하고 다음단계의 작업을 진행함
        - fromuser : export 할 당시의 object의 소유자 지정
        - touser : import 할 object의 새 소유자 지정
        - tables : import 할 테이블 이름 지정
        - parfile : import 옵션을 미리 지정한 파라미터 파일지정

      나. 사용예정

    imp system/oracle file=/backup/export/test01.dmp ignore=y full=y

    imp system/oracle file=/backup/export/test02.dmp \ 
    fromuser=scott touser=hr ignore=y

    imp system/oracle file=/backup/export/test03.dmp full=y show=y log=test03.log


    참고 : export/import 계정
    import 할 때 사용하는 계정은 export 할 때 사용한 계정이어야 한다. 이 계정이 같지 않으면 import 수행 시 오류가 발생한다.만일 export 계정을 잊었다면 덤프파일을 vi 편집기로 열어 확인할 수 있다.(2번째 줄)
    참고 : import 작업 중 에러발생 시
    import 작업을 진행하던 도중 에러가 발생해 같은 작업을 반복하게 되면, import 대상이 되는 테이블(제약조건이 없는)에 데이터가 중복 저장될 수 있다. 그러므로 같은 작업을 반복시에는 import 대상이 되는 테이블의 내용을 지우고(drop 또는 truncate) 진행해야 한다.
    참고 : SYS 계정으로 생성된 Object export
    일반적으로 SYS계정에서 생성된 객체는 export 명령어로 백업할 수 없으므로 주의해야 한다.
    (단, 경우에 따라서 system 계정으로 백업이 가능하기도 하다)

    4. Import 대상 서버에서 필요한 사전 작업
      가. Export 한 서버와 동일한 Tablespace 생성
      나. 충분한 크기의 Temporary Tablespace 확보
      다. Export 한 서버와 동일한 사용자 생성


    참고 : 오라클 레퍼런스 사이트

    Export and Import Modes : http://docs.oracle.com/cd/B19306_01/server.102/b14215/exp_imp.htm#i1004890

    Export Parameters : http://docs.oracle.com/cd/B19306_01/server.102/b14215/exp_imp.htm#CEGFIAGE

    Import Parameters : http://docs.oracle.com/cd/B19306_01/server.102/b14215/exp_imp.htm#i1021478  

    * export

    - 전체 데이터베이스가 export 방법

    ex.) C:>exp userid=system/manager file='C:/full.dmp' full=y
    - 서비스명 포함
    ex.) C:>exp userid=system/manager@서비스명 file='c:/full.dmp' full=y
    - exp-00091 불완전한 통계를 엑스포트 중입니다. 메시지 출력시 (oracle 버전 확인과 NLS_LANG가 달라서 발생)
    ex.) C:>exp userid=system/manager@서비스명 file='c:/full.dmp' full=y statistics=none
    이관 데이터에는 상관없고 실행 후 dbms_stats.gather_schema_stats 를 사용하여 통계정보를 생성하면 됨


    - user별 EXPORT하는 방법.
    ex.) C:>exp userid=scott/tiger file='C:scott.dmp'
    - SYSTEM/MANAGER로 접속한 DBA가 여러 user소유의 오브젝트들을 EXPORT 하는 방법
    ex.) C:>exp userid=system/manager owner=scott file='C:scottuser.dmp'
    - system user로 다른 유저의 table 몇 개만 Export하는 방법
    C:>exp userid=system/manager file='C:exp.dmp' tables=(scott.EMP, scott.DEPT)
    => 위와 같이 table의 schema(user)명까지 지정해야만 export가 성공합니다.
    - scott user로 table 몇 개만 EXPORT하는 예
    C:>exp userid=scott/tiger file='C:exp.dmp' tables=(EMP, DEPT) log=exp.log
    추가 옵션
    full=y : 전체 데이터 추출 여부 (기본값 n)
    direct : 직접경로 방식으로 export(기본값 n)
    indexs : 인덱스 포함 여부(기본값 y)
    triggers : 트리거 포함 여부(기본값 y)
    rows=n : 오브젝트에 대한 정의만 export (테이블의 저장된 데이터는 export 제외)
    buffer : 작업 단위 크기 설정
    compress : 익스텐트 통합여부 지정(기본값 y)
    grants : 오브젝트 권한 설정에 대한 정보 추출 여부(기본값 y)
    log : 로그를 저장할 파일 지정
    row : 테이블의 데이터 추출 여부(기본값 y)
    consistents : 대상 테이블의 읽기 일관성 지정(기본값 n)
    prfile : 필요한 옵션을 파라미터 파일에 설정한 후 해당 파라미터 파일을 export 시 적용
    query : 쿼리 조건에 맞는 데이터만 적용 ex) query=\"where id\=100\"

    Export 활용

    TIP1.COMPRESS 옵션은 모든 익스텐트를 하나의 익스텐트로 통합하여 구성하는 옵션이다. 이 경우 하나의 데이터 파일로만 모든 데이터가

    적재되기 때문에 I/O분산 측면에서 분리하다. 그러므로 실제 운영에서는 이와 같이 익스텐트들이 통합되는 것은 좋지 않으므로 Export를 수행할

    경우 반드시 COMPRESS 옵션을 N으로 설정하기를 권장한다.

    TIP 2. DIRECT 옵션은 오라클 메모리 영역인 SGA를 사용하지 않고 Export를 수행하는 옵션이다. 직접 경로로 수행하여 추출된 파일은

    Import시에도 기본적으로 직접 경로로 적재된다. 그러므로 DIRECT옵션을 Y로 설정하면 추출 및 적재 잡업시 보다 빠른 속도를 보장받을

    수 있다.

    TIP 3.CONSISTENTS 옵션은 Export를 수행한 시점의 데이터를 추출하게 된다. Export 중 변경된 데이터는 언두 데이터를 이용하여 이전 값을

    추출하게 되는데 이때 'Snap Shot Too Old' 에러가 발생하기 쉽다. 그래서 CONSISTENTS옵션을Y로 설정하기를 권장한다.

    TIP 4. STATISTICS 옵션은 oracle 9i버전에서 특수 통계정보를 수집하는 옵션이다. "EXP-00091: 불완전한 통계를 엑스포트 중입니다." 에러가

    발생하지 않게 하기 위해서는STATISTICS옵션을NONE으로 설정하기를 권장한다.


    * import
    -전체 데이터베이스가 IMPORT됩니다. (Full Level)
    C:>imp userid=system/manager file='C:full.dmp' full=y

    - scott의 유저 IMPORT를 실행 합니다.(User Level)
    C:>imp userid=scott/tiger file='C:scott.dmp'

    - 다른 계정으로 IMPORT하기
    scott유저의 데이터를 EXPORT받아서 test 유저에게 IMPORT하는 예제 입니다.
    C:>exp userid=system/manager file='C:scott.dmp' owner=scott
    C:>imp userid=system/manager file='C:scott.dmp' fromuser=scott touser=test

    ===================================================================================
    오라클 홈디렉토리 또는 Base 디렉토리에 가시면 bin 디렉토리가 있습니다.
    bin 디렉토리 안에는 여러가지 툴이 있는데 그중에 exp 와 imp 가 mysql dump 와 같은 기능을
    가지고 있습니다.
    exp help=y 하시면 도움말이 나옵니다.
    대화형식으로 백업 하시려면 exp 만 치시면 순서대로 필요한 사항을 입력하시면 dump 가능하
    구요
    예제) exp scott/tiger file=/home/backup/daily_backup.dmp
    log=/home/backup/daily_backup.log grants=y
    물론 위의 디렉토리에는 oracle user의 쓰기 권한이 있어야 겠지요.
    imp 인경우도 도움말을 보시면 편합니다.
    예제) imp scott/tiger file=/home/backup/daily_backup.dmp
    log=/home/backup/daily_backup_imp.log ignore=y grants=y buffer=2048000 full=y
    여기서 log 는 imp , exp 시 남는 log 입니다. table 이 정상적으로 export 또는 import 되는지
    보여주는 옵션입니다.

     

     

    exp는 보조 백업의 의미로 테이블 단위의 복구가 필요할 때 주로 사용한다.

    하지만 장애시점까지의 복구가 아니라 백업받은 시점으로의 복구만 가능하다.

    0. exp/imp 제한
       - Export 파일(.dmp)을 네트워크를 통해 전송할 때는 반드시

         이진(Binary) 형태로 전송
       - SQL*Net 을 이용해서 exp/imp를 수행할 수 있음

         (exp userID/password@TNS_ALIAS ...)
       - Stored Procedure, 함수, 패키지를 Import 할 때 재 컴파일의

         필요성이 생길 수 있음
       - exp 도중에 시퀀스(sequence)를 사용하게 된다면,

         시퀀스 번호는 skip 될 수 있음
       - imp할 때 Long Type의 컬럼은 언제나 성공적으로 수행되는 것은 아님

         (imp 대신 copy 명령 사용)

    1. 일반적으로 많이 사용하는 exp/imp 명령어

    exp userid/password file=exp.dmp owner=vnet direct=y buffer=10240000 grants=y compress=n constraints=y indexes=y rows=y feedback=10000 statistics=none log=vnet.log

    imp system/qkrgustlr file=c:\vnet.dmp fromuser=vnet touser=vnet commit=y ignore=y buffer=10240000 grants=y constraints=y indexes=y rows=y feedback=10000 log=c:\imp.log
       ---------------------------------------
       % exp userid/password file=./dmp/TEST.dmp          \
             direct=y buffer=10240000 grants=y            \
             compress=n constraints=y indexes=y rows=y    \
             triggers=n tables=XXXX,YYYY,ZZZZ             \
             feedback=10000 log=./log/exp_test.log

       % imp dbaid/password file=./dmp/TEST.dmp           \
             fromuser=userid touser=otherid               \
             commit=y ignore=y buffer=10240000 grants=y   \
             constraints=y indexes=y rows=y               \
             tables=XXXX,YYYY,ZZZZ                        \
             feedback=10000 log=./log/imp_test.log

    2. pipe를 통하여 백업 & 압축하는 exp/imp 명령어
       --------------------------------------------
       % rm /tmp/exp_test
       % /usr/sbin/mknod /tmp/exp_test p
       % compress </tmp/exp_test> ./dmp/TEST.dmp.Z &
       % exp userid/password file=/tmp/exp_test           \
             direct=y buffer=10240000 grants=y            \
             compress=n constraints=y indexes=y rows=y    \
             triggers=n tables=XXXX,YYYY,ZZZZ             \
             feedback=10000 log=./log/exp_test.log
       % rm /tmp/exp_test

       % rm -f /tmp/imp_test
       % /usr/sbin/mknod /tmp/imp_test p
       % uncompress<./dmp/TEST.dmp.Z> /tmp/imp_test &
       % imp dbaid/password file=/tmp/imp_test            \
             fromuser=userid touser=otherid               \
             commit=y ignore=y buffer=10240000 grants=y   \
             constraints=y indexes=y rows=y               \
             tables=XXXX,YYYY,ZZZZ                        \
             feedback=10000 log=./log/imp_test.log
       % rm -f /tmp/imp_test

        참고) exp와 imp를 연결하여 실행
              ftp가 지원되지 않고 TNS로 연결이 가능한 경우 사용한다.
              (파이프를 이용하여 exp하고 곧바로 imp로 연결하여 실행)
              % vi exp_and_imp.sh

                rm  /tmp/exp_node
                /usr/sbin/mknod /tmp/exp_node p
                exp dbaid/password@TNS_ALIAS FILE=/tmp/exp_node OWNER=us_test \

                    INDEXES=n BUFFER=204800000 DIRECT=y LOG=exp_test.log &
                imp dbaid/password FILE=/tmp/exp_node FROMUSER=us_test        \

                    TOUSER=us_test INDEXES=n COMMIT=y BUFFER=204800000        \

                    FEEDBACK=100000 IGNORE=y LOG=imp_test.log
                rm  /tmp/exp_node
                     :wq


    3. 파티션된 테이블의 파티션 exp 명령어
       --------------------------------------------
       % exp userid/password file=./dmp/TEST.dmp                     \
             direct=y buffer=10240000 grants=y                       \
             compress=n constraints=y indexes=y rows=y               \
             triggers=n tables=XXX:PT_XXX_2007,YYY:PT_YYY_2007       \
             feedback=10000 log=./log/exp_test.log

       % imp dbaid/password file=./dmp/TEST.dmp                      \
             fromuser=userid touser=otherid                          \
             commit=y ignore=y buffer=10240000 grants=y              \
             constraints=y indexes=y rows=y                          \
             tables=XXX:PT_XXX_2007,YYY:PT_YYY_2007                  \
             feedback=10000 log=./log/imp_test.log
       % rm -f /tmp/imp_test

    4. FILESIZE를 이용한 SPLIT exp/imp 명령(8i)
       --------------------------------------------
       % exp userid/password file=./dmp/TEST01.dmp,                  \
                                  ./dmp/TEST02.dmp,                  \
                                  ./dmp/TEST03.dmp                   \
             direct=y buffer=10240000 grants=y                       \
             compress=n constraints=y indexes=y rows=y               \
             feedback=10000 filesize=100M log=./log/exp_test.log     \
             tables=TEST

       % imp dbaid/password file=./dmp/TEST01.dmp,                   \
                                 ./dmp/TEST02.dmp,                   \
                                 ./dmp/TEST03.dmp                    \
             fromuser=userid touser=otherid                          \
             commit=y ignore=y buffer=10240000 grants=y              \
             constraints=y indexes=y rows=y                          \
             tables=TEST                                             \
             feedback=10000 log=./log/imp_test.log

    5. remote에서 exp하는 명령어
       --------------------------------------------
       % exp userid/password@TNS_ALIAS file=./dmp/TEST.dmp          \
             direct=y buffer=10240000 grants=y                      \
             compress=n constraints=y indexes=y rows=y              \
             triggers=n tables=XXXX,YYYY,ZZZZ                       \
             feedback=10000 log=./log/exp_test.log

     

    참고) \는 UNIX에서 다음 라인과 이어진다는 표시의 기호임.

     

    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
    , |

    - 업무 파티션을 하고자 할 경우 각 업무별로 장애가 발생하지 않는 평시에
      RACDB1으로 접속하여 사용할 것인지 RACDB2로 접속하여 사용할 것인지를 결정하여야 함.
    - 업무파티션의 기준은 각각의 노드에서 테이블 단위의 DML(INSERT,UPDATE,DELETE)이
      최소화되도록 설계하여야 함.

     

    1. 리스너 설정(listener.ora)
       -----------------------
       # Local Listener를 사용하도록 설정(RACDB1)

       LISTENER_HOSTNAME1 =
         (DESCRIPTION_LIST =
           (DESCRIPTION =
             (ADDRESS = (PROTOCOL = TCP)(HOST = VIP_HOSTNAME1)(PORT = 1521)(IP=FIRST))
             (ADDRESS = (PROTOCOL = TCP)(HOST = HOSTNAME1)    (PORT = 1521)(IP=FIRST))
             (ADDRESS = (PROTOCOL = IPC)(KEY = extproc))
           )
         )
       
       SID_LIST_LISTENER_HOSTNAME1 =
         (SID_LIST =
           (SID_DESC =
             (SID_NAME = PLSExtProc)
             (ORACLE_HOME = /oracle/app/oracle/product/10.2.0/db_1)
             (PROGRAM = extproc)
           )
         )

     

    2. TNS(tnsnames.ora) 설정 필요 - JDBC OCI 방식
       ----------------------------------------------

        # JDBC OCI 방식으로 설정하여 사용하고자 할 경우 TNS설정이 필요하며
        # RACDB에 접속하는 방식은 SQL*Net을 사용하게 됨
        # (SELECT인 것만 FAILOVER할 수 있도록 설정한 예, 주로 많이 사용하는 설정)

       TNS_RACDB1 = (DESCRIPTION=
                     (LOAD_BALANCE=OFF)
                     (FAILOVER=ON)
                     (ADDRESS=(PROTOCOL=TCP)(HOST=VIP_HOSTNAME1)(PORT=1521))
                     (ADDRESS=(PROTOCOL=TCP)(HOST=VIP_HOSTNAME2)(PORT=1521))
                     (CONNECT_DATA = (SERVER = DEDICATED)
                                     (SERVICE_NAME = RACDB)
                                     (FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC))
                     )
                 )
       
       TNS_RACDB2 = (DESCRIPTION=
                     (LOAD_BALANCE=OFF)
                     (FAILOVER=ON)
                     (ADDRESS=(PROTOCOL=TCP)(HOST=VIP_HOSTNAME2)(PORT=1521))
                     (ADDRESS=(PROTOCOL=TCP)(HOST=VIP_HOSTNAME1)(PORT=1521))
                     (CONNECT_DATA = (SERVER = DEDICATED)
                                     (SERVICE_NAME = RACDB)
                                     (FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC))
                     )
                 )

     

         1]  RACDB1에 접속하고자 하는 설정
              jdbc:oracle:oci:@TNS_RACDB1

     

         2  RACDB2 접속하고자 하는 설정
              jdbc:oracle:oci:@TNS_RACDB2


    3. TNS(tnsnames.ora) 설정 필요없음 - JDBC thin 방식
       -----------------------------------------------

       1] RACDB1에 접속하고자 하는 설정
          jdbc:oracle:thin:@(DESCRIPTION=
                               (FAIL_OVER=ON)
                               (LOAD_BALANCE=OFF)
                               (ADDRESS=(PROTOCOL=TCP)(HOST=100.100.52.111)(PORT=1521)) # RACDB1 Virtual-IP 사용
                               (ADDRESS=(PROTOCOL=TCP)(HOST=100.100.52.112)(PORT=1521)) # RACDB2 Virtual-IP 사용
                               (CONNECT_DATA=(SERVICE_NAME=RACDB)))

     

       2] RACDB2에 접속하고자 하는 설정
          jdbc:oracle:thin:@(DESCRIPTION=
                               (FAIL_OVER=ON)
                               (LOAD_BALANCE=OFF)
                               (ADDRESS=(PROTOCOL=TCP)(HOST=100.100.52.112)(PORT=1521)) # RACDB2 Virtual-IP 사용
                               (ADDRESS=(PROTOCOL=TCP)(HOST=100.100.52.111)(PORT=1521)) # RACDB1 Virtual-IP 사용
                               (CONNECT_DATA=(SERVICE_NAME=RACDB)))

    Posted by redkite
    , |

    RAC Archive log 모드를 NoArchive log모드로 변경

     

    아카이브 로그의 모드를 변경할 때는 cluster_database를 False로 변경한 후

    여러 RAC 노드 중 한 개의 노드에서만 아카이브 로그의 모드를 변경 작업하고,

    cluster_database를 True로 변경한 후 startup하면 모든 RAC노드에 함께 적용된다.

     

    -----------------------------------------------
    [방법1] 여러 Parameter를 함께 변경할 경우
    -----------------------------------------------

    환경 : AIX5L Oracle10gR2 EE RAC 2-NODE, Raw Device

           (spfile도 Raw Device에 생성하여 양쪽노드가 공유)

     

    0] 모든 노드 : DB SHUTDOWN

       srvctl stop instance -d RACDB -i RACDB1

       srvctl stop nodeapps -n dbhost1

       srvctl stop instance -d RACDB -i RACDB2

       srvctl stop nodeapps -n dbhost2

       (CRS stop하지 않음)

     

    1] NODE 1 : DB STARTUP 상태에서 시작


       SQL> connect / as sysdba
       SQL> STARTUP NOMOUNT
       Archive log mode 확인
       SQL> archive log list
            Database log mode              Archive Mode
            Automatic archival             Enabled
            Archive destination            /arch/RACDB1
                :

     

    2] NODE 1 : spfile에서 pfile 생성 후 SHUTDOWN
       SQL> !cat initRACDB1.ora
            SPFILE='/dev/rspfileRACDB'

       SQL> !cp initRACDB1.ora initRACDB1.ora.20100316
       SQL> create pfile from spfile;

       SQL> shutdown immediate

     

    3] NODE 1 : initRACDB1.ora의 cluster_database 주석처리 후 저장
       SQL> !vi initRACDB1.ora
             # cluster_database           = TRUE

             RACDB1.log_archive_dest_1  = "location=/arch/RACDB1 MANDATORY"
             RACDB2.log_archive_dest_1  = "location=/arch/RACDB2 MANDATORY"
             RACDB1.log_archive_format  = RACDB%t_%s_%r.arc
             RACDB2.log_archive_format  = RACDB%t_%s_%r.arc

                 # LOG_ARCHIVE_START=TRUE = TRUE # 10g에서는 사용하지 않음

     

    4] NODE 1 : Noarchivelog mode 적용
       SQL> startup mount
       SQL> alter database noarchivelog;
       SQL> alter database open;
       SQL> shutdown immediate

     

    5] NODE 1 : initRACDB1.ora의 주석 처리했던 cluster_database를 원상복구 후 저장
       SQL> !vi initRACDB1.ora
             cluster_database           = TRUE

             RACDB1.log_archive_dest_1  = "location=/arch/RACDB1 MANDATORY"
             RACDB2.log_archive_dest_1  = "location=/arch/RACDB2 MANDATORY"
             RACDB1.log_archive_format  = RACDB%t_%s_%r.arc
             RACDB2.log_archive_format  = RACDB%t_%s_%r.arc

             # LOG_ARCHIVE_START=TRUE = TRUE # 10g에서는 사용하지 않음

     

    6] NODE 1 : spfileRACDB를 다시 생성 후 initRACDB1.ora 원상복구
       SQL> create SPFILE='/dev/rspfileRACDB' from pfile;

       SQL> !mv initRACDB1.ora.20100316 initRACDB1.ora
       SQL> !cat initRACDB1.ora
            SPFILE='/dev/rspfileRACDB'

     

    7] 모든 노드 : 데이터베이스 startup

       srvctl start nodeapps -n dbhost1

       srvctl start instance -d RACDB -i RACDB1

       srvctl start nodeapps -n dbhost2

       srvctl start instance -d RACDB -i RACDB2

       Archive log mode 확인

       SQL> connect / as sysdba
       SQL> archive log list
            Database log mode              No Archive Mode
            Automatic archival             Disabled
            Archive destination            /arch/RACDB1
               :

     

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

    [방법2] Parameter 중에 CLUSTER_DATABASE 만 변경할 경우

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

    다른 init Parameter를 바꿀 필요없을 때는 다음과 같이 작업한다.

     

    1] NODE 1 : DB STARTUP 상태에서 시작
       SQL> connect / as sysdba
       Archive log mode 확인
       SQL> archive log list
            Database log mode              Archive Mode
            Automatic archival             Enabled
            Archive destination            /arch/RACDB1
               :

     

    2] NODE 1 : spfile에 cluster_database=FALSE 설정
       SQL> ALTER SYSTEM SET CLUSTER_DATABASE=FALSE SCOPE=spfile;

     

    3] 모든 노드 : RAC 노드 1, 2 모두 shutdown
       srvctl stop instance -d RACDB -i RACDB1

       srvctl stop nodeapps -n dbhost1

       srvctl stop instance -d RACDB -i RACDB2

       srvctl stop nodeapps -n dbhost2

       (CRS stop하지 않음)

     

    4] NODE1 : Noarchivelog mode 적용하고 Open한 후 SHUTDOWN
       SQL> connect / as sysdba
       SQL> startup mount
       SQL> alter database noarchivelog;
       SQL> alter database open;
       SQL> shutdown immediate

     

    5] NODE1 : spfile에 cluster_database=TRUE 설정
       SQL> connect / as sysdba
       SQL> startup mount
       SQL> ALTER SYSTEM SET CLUSTER_DATABASE=TRUE SCOPE=spfile; 
       SQL> shutdown immediate

     

    6] 모든 노드 : 데이터베이스 startup 
       srvctl start nodeapps -n dbhost1

       srvctl start instance -d RACDB -i RACDB1

       srvctl start nodeapps -n dbhost2

       srvctl start instance -d RACDB -i RACDB2

       No Archive log mode 확인

        SQL> archive log list
            Database log mode              No Archive Mode
            Automatic archival             Disabled
            Archive destination            /arch/RACDB1

     

    Posted by redkite
    , |

    RAC NoArchive log 모드를 Archive log모드로 변경

     

    아카이브 로그의 모드를 변경할 때는 cluster_database를 False로 변경한 후

    여러 RAC 노드 중 한 개의 노드에서만 아카이브 로그의 모드를 변경 작업하고,

    cluster_database를 True로 변경한 후 startup하면 모든 RAC노드에 함께 적용된다.

     

    -----------------------------------------------
    [방법1] 여러 Parameter를 함께 변경할 경우
    -----------------------------------------------

    환경 : AIX5L Oracle10gR2 EE RAC 2-NODE, Raw Device
           (spfile도 Raw Device에 생성하여 양쪽노드가 공유)

     

    0] 모든 노드 : DB SHUTDOWN
       srvctl stop instance -d RACDB -i RACDB1

       srvctl stop nodeapps -n dbhost1

       srvctl stop instance -d RACDB -i RACDB2

       srvctl stop nodeapps -n dbhost2

       (CRS stop하지 않음)

     

    1] NODE 1 : DB STARTUP 상태에서 시작
       SQL> connect / as sysdba
       SQL> STARTUP NOMOUNT
       Archive log mode 확인
       SQL> archive log list
            Database log mode              No Archive Mode
            Automatic archival             Disabled
            Archive destination            /arch/RACDB1
               :

     

    2] NODE 1 : spfile에서 pfile 생성 후 SHUTDOWN
       SQL> !cat initRACDB1.ora
            SPFILE='/dev/rspfileRACDB'
       SQL> !cp initRACDB1.ora initRACDB1.ora.20100316
       SQL> create pfile from spfile;
       SQL> shutdown immediate

     

    3] NODE 1 : initRACDB1.ora의 cluster_database 주석처리 후 저장
       SQL> !vi initRACDB1.ora
             # cluster_database           = TRUE

             RACDB1.log_archive_dest_1  = "location=/arch/RACDB1 MANDATORY"
             RACDB2.log_archive_dest_1  = "location=/arch/RACDB2 MANDATORY"
             RACDB1.log_archive_format  = RACDB%t_%s_%r.arc
             RACDB2.log_archive_format  = RACDB%t_%s_%r.arc
             #LOG_ARCHIVE_START  = TRUE # 10g에서 없어짐
             
    4] NODE 1 : Archivelog mode 적용하고 Open한 후 SHUTDOWN
       SQL> startup mount
       SQL> alter database archivelog;
       SQL> alter database open;
       SQL> shutdown immediate

     

    5] NODE 1 : initRACDB1.ora의 주석 처리했던 cluster_database를 원상복구 후 저장
       SQL> !vi initRACDB1.ora
             cluster_database           = TRUE
             RACDB1.log_archive_dest_1  = "location=/arch/RACDB1 MANDATORY"
             RACDB2.log_archive_dest_1  = "location=/arch/RACDB2 MANDATORY"
             RACDB1.log_archive_format  = RACDB%t_%s_%r.arc
             RACDB2.log_archive_format  = RACDB%t_%s_%r.arc

     

    6] NODE 1 : spfileRACDB를 다시 생성 후 initRACDB1.ora 원상복구
       SQL> create SPFILE='/dev/rspfileRACDB' from pfile;

       SQL> !mv initRACDB1.ora.20100316 initRACDB1.ora
       SQL> !cat initRACDB1.ora
            SPFILE='/dev/rspfileRACDB'

     

    7] 모든 노드 : 데이터베이스 startup

       srvctl start nodeapps -n dbhost1

       srvctl start instance -d RACDB -i RACDB1

       srvctl start nodeapps -n dbhost2

       srvctl start instance -d RACDB -i RACDB2

       Archive log mode 확인

       SQL> connect / as sysdba

       SQL> archive log list
            Database log mode              Archive Mode
            Automatic archival             Enabled
            Archive destination            /arch/RACDB1
               :

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

    [방법2] Parameter 중에 CLUSTER_DATABASE 만 변경할 경우

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

    다른 init Parameter를 바꿀 필요없을 때는 다음과 같이 작업한다.

    1] NODE 1 : DB STARTUP 상태에서 시작
       SQL> connect / as sysdba
       Archive log mode 확인
       SQL> archive log list
            Database log mode              No Archive Mode
            Automatic archival             Disabled
            Archive destination            /arch/RACDB1
                    :

     

    2] NODE 1 : spfile에 cluster_database=FALSE 설정
       SQL> ALTER SYSTEM SET CLUSTER_DATABASE=FALSE SCOPE=spfile;

     

    3] 모든 노드 : RAC 노드 1, 2 모두 shutdown
       srvctl stop instance -d RACDB -i RACDB1

       srvctl stop nodeapps -n dbhost1

       srvctl stop instance -d RACDB -i RACDB2

       srvctl stop nodeapps -n dbhost2

       (CRS stop하지 않음)

     

    4] NODE 1 : archivelog mode 적용하고 Open한 후 SHUTDOWN
       SQL> connect / as sysdba
       SQL> startup mount
       SQL> alter database archivelog;
       SQL> alter database open;
       SQL> shutdown immediate

     

    5] NODE1 : spfile에 cluster_database=TRUE 설정
       SQL> connect / as sysdba
       SQL> startup mount
       SQL> ALTER SYSTEM SET CLUSTER_DATABASE=TRUE SCOPE=spfile; 
       SQL> shutdown immediate

     

    6] 모든 노드 : 데이터베이스 startup 
       srvctl start nodeapps -n dbhost1

        srvctl start instance -d RACDB -i RACDB1

       srvctl start nodeapps -n dbhost2

       srvctl start instance -d RACDB -i RACDB2

       Archive log mode 확인

         SQL> archive log list
            Database log mode              Archive Mode
            Automatic archival             Enabled
            Archive destination            /arch/RACDB1

    Posted by redkite
    , |

    최근에 달린 댓글

    최근에 받은 트랙백

    글 보관함