 |
|
난이도 : 중급 Matthias Nicola, DB2/XML Performance, IBM Silicon Valley Lab
2008 년 4 월 15 일
DB2 9
은 XML 데이터가 고유한 계층 포맷에 저장, 질의되는 pureXML이라는 기능을 지원한 첫 제품입니다. DB2는 XML 데이터를 질의하기 위해 SQL/XML과 XQuery라는 두 가지 언어를 제공합니다.
또한 DB2 9은 XML 인덱싱 기능과 XML 스키마 유효성 지원이 특히 향상됐습다. 이 글에서는 DB2
고유의 XML 데이터 처리 성능에 대한 설명과 함께, XML에 특화된 주요 성능 팁을 추가로
제공합니다. 본 글은 DB2 9.5용으로 추가 업데이트됐습니다.
소개
DB2 9에서 지원되는 pureXML은 XML 데이터를 관리하는 효율적이고 다양한 기능을 제공한다.
XML 애플리케이션에 있어 성능은 매우 중요한 요소다. DBA와 애플리케이션 디자이너들은 XML
애플리케이션의 성능이 높은 수준으로 유지되도록 하기 위해 노력한다. 일단 균형적인
CPU/메모리/디스크 구성, 테이블 스페이스와 버퍼 풀 튜닝, 잠금, 로깅, 질의 실행 플랜 등을
위한 전통적인 DB2 성능 지침을 살펴보자. 이 모든 주제는 이미 이전에 DB2 관련 글(
참고자료
참조)에서 다뤘던 내용이지만, 여전히 DB2에서 XML 데이터를 관리할 때 숙지해야 할 사항이다.
다행히 이 몇 가지 이슈는 자동 저장소와 자가 튜닝(self-tuning) 메모리 관리 등과 같은
DB2의 자율 기능으로 처리된다. 이 기능들은 대부분의 애플리케이션에서 높은 수준의 성능을 제공하므로 약간의 수동 처리만 하면 된다. 하지만 높은 성능을 요구하는 XML 애플리케이션은
성능 통합을 추가로 수행할 경우 도움이 될 것이다. 이 글은 DB2 9에서 XML과 관련된
애플리케이션의 최적 성능을 얻기 위한 팁과 지침을 제공한다.
아래 목록은 이 글에서 다룰 15개의 XML 성능 팁(순서는 중요치 않다)이다. 이들 15개 팁은
다양한 분야를 다루지만 경험상 성능 문제가 있는 애플리케이션은 이 중 하나나 둘 정도의 팁만 적용해도
원하는 성능 향상을 이룰 수 있다.
독자들이 기본 DB2 관리와 성능 연습, DB2 pureXML 지원에 대해 익숙하다는 가정 하에 이
성능 팁을 설명한다. 예를 들어 XML 행, XML 인덱스 및 SQL/XML과 XQuery로 XML
데이터를 질의하는 방법을 알아야 한다. 모든 필요한 사전지식은 developerWorks에 발행한
글(
참고자료
참조)에서 다뤘으니 참조하기 바란다.
DB2 XML 성능 팁
팁 1: XML 문서 단위 현명하게 선택하기
XML 애플리케이션과 XML 문서 구조를 디자인할 때 특히 단일 XML 문서에 어떤 비즈니스 데이터를
함께 저장할 것인지 정의해야 한다. 예를 들어 아래 부서 테이블에서 부서(중간 단위) 당 하나의
XML 문서를 사용한다. 애플리케이션이 데이터에 접근, 처리하는 수준의 단위를 가지고 있다면 적절히
사용한 것이다. 또는 많은 부서를 단일 XML 문서에 결합하도록 결정할 수도 있다. 예를
들어 모든 것을 하나의 유닛(굵은 단위)에 포함시킬 수 있다. 하지만 한 번에 한 부서만 처리하는
전통적인 프로세스일 경우, 최적의 선택은 아니다.
테이블 1. dept 테이블 만들기( unitID char(8), deptdoc xml)
| unitID | deptdoc |
|---|
| WWPR |
<dept deptID='PR27'>
<employee id='901'>
<name>Jim Qu</name>
<phone>408 555 1212</phone>
</employee>
<employee id='902'>
<name>Peter Pan</name>
<office>216</office>
</employee>
</dept>
|
| | WWPR |
<dept deptID='V15'>
<employee id='673'>
<name>Matt Foreman</name>
<phone>416 891 7301</phone>
<office>216</office>
</employee>
<description>This dept supports sales world wide</description>
</dept> |
| | S-USE | ... | | ... | ... |
또한 한 직원 당 한 XML 문서에(미세한 단위) 직원 별로 "dept" 속성을 추가하여 그 직원이
어떤 부서 소속인지 나타낼 수 있도록 결정할 수 있다. 이는 직원들 각자가 흥미에 따라 비즈니스
객체를 사용하여 같은 부서의 다른 직원들이 독립적으로 접근하고 처리할 수 있다면 매우 훌륭한
선택이다. 하지만 애플리케이션이 전형적인 방식으로 한 부서의 모든 직원을 한꺼번에 처리한다면 한 부서
당 하나의 XML 문서가 더 나은 선택이 된다.
특히 단일 문서의 많은 독립 비즈니스 객체들이 일괄 처리되는 것은 바람직하지 않다. DB2는 XML
데이터를 통해 인덱스를 사용한 문서 레벨 별로 필터링한다. 그러므로 XML 문서가 더 미세한
단위일수록 인덱스 기반 접근에서 더 많은 잠재적 이점을 얻을 수 있다. 또한 애플리케이션이 DOM
파서를 사용해 DB2에서 찾아온 XML을 취하면 작은 문서가 더 나은 성능을 갖게 될 것이다.
XML 문서 디자인과 관련해 일반적인 질문은 속성 대 요소를 언제 사용할 것이며 이 선택이 성능에
어떤 영향을 미치는지에 관한 것이다. 이는 성능에 관한 질문이기보다는 데이터 모델링에 관한 질문이다.
그러므로 이 질문은 XML의 전신인 SGML만큼이나 오래된 질문이고 관련자들 사이에서 뜨거운
감자였다. 하지만 여기서 중요한 사실은 XML 요소가 반복될 수 있고 중첩될 수 있기 때문에 속성보다
더 유연하다는 것이다. 예를 들어 부서 문서에서 직원들에게 하나 이상의 전화번호가 있을 때 이를 다
받아들일 수 있는 "phone" 요소를 사용한다. 이는 또한 나중에 전화번호를 나눠야 할 때(예를
들어 "phone" 요소는 나라 코드, 지역 코드, 내선 등으로 자식 요소를 가질 수 있다) 좋다.
그런데 "phone"이 직원 요소의 속성이라면 직원 당 하나로만 존재하므로 자식 요소를 추가할 수
없기에 시간에 따라 스키마 발전을 막을 것이다. 속성 없이 모든 데이터를 만든다 해도 반복할
수도(요소 당), 하위 필드를 가질 수도 없기 때문에 데이터 아이템에는 직관적인 선택이 될 수밖에
없다. 속성은 시작 태그와 끝 태그가 있는 요소와는 달리 단일 태그만 가지고 있으므로 더 짧은 XML을
만들 수 있다. DB2에서 속성은 요소처럼 쉽게 질의, 술어, 인덱스 정의에 사용될 수 있다. 속성이
요소보다 덜 확장적이기 때문에 DB2는 특정 저장소와 접근 최적화에 적용될 수 있다. 이는 속성에서
요소로 변환하는 데 따른 이점이라기보다는 추가 성능의 문제로 고려하는 것이 좋다. 특히 데이터
모델링을 실제 요소로 하려고 할 때 그렇다.
한 마디로, 예상되는 접근의 우수 단위에 맞게 XML 문서 단위를 선택하라.
자신이 없다면 더 미세한 단위와 작은 XML 문서를 선택하는 것이 대체로 더 낫다.
팁 2: 더 나은 성능을 위해 DMS와 대용량 페이지 사용하기
DMS(데이터베이스로 관리되는 테이블 공간: Database managed table spaces)는
SMS(시스템으로 관리되는 테이블 공간: system managed table spaces)로
운영하는 것보다 더 높은 성능을 제공한다. 관계형 데이터에서 그렇고 XML 읽기와 쓰기 접근에서 더
그렇다. DB2 9에서 새로 만들어진 테이블 공간은 기본으로 DMS다. 또한 자동 저장소에 DMS
테이블 공간을 사용하도록 권유하므로 DMS 컨테이너는 수동 개입 없이도 필요한 만큼 늘어난다.
XML 문서가 테이블 공간의 단일 페이지에 들어가기에 너무 크다면 DB2는 문서를 다중 영역으로
분리하여 다중 페이지에 저장한다. 이는 DB2가 XML 문서를 문서 당 최대 공간인 2GB까지 처리할
수 있게 한다.
대체로 문서당 영역(나누기)이 작을수록 특히 삽입과 전체 문서 찾아오기 시에 더 나은 성능을 보인다.
페이지에 문서가 맞지 않으면 문서 당 나뉘는 수는 페이지 크기를 따른다(4KB, 8KB, 16KB,
32KB). 테이블 공간의 페이지 크기가 클수록 문서당 잠재적으로 나뉘는 수가 작아진다. 예를 들어
주어진 문서가 40개의 4KB 페이지로 나뉜다고 하자. 그렇게 되면 같은 문서가 20개의 8KB
페이지에 저장되거나 10개가 16KB에, 5개가 32KB 페이지에 각각 저장된다. XML 문서가
선택된 페이지 크기보다 현저히 작으면 다중의 작은 문서가 단일 페이지에 저장될 수 있으므로 낭비되는
공간이 없을 것이다.
어림잡아 평균적으로 예상되는 문서 크기의 두 배보다 작지 않게, 32KB를 최대로 XML
데이터의 페이지 크기를 선택하자. 관계형과 XML 데이터, 데이터와 인덱스의 단일 페이지
크기를 사용한다면 XML 데이터에 32KB 페이지 크기가 적당하겠지만 관계형 데이터와 인덱스
접근에는 불리할 것이다. 이런 경우, 관계형 데이터와 인덱스에게는 16KB나 6KB 페이지가
더 나을 것이다.
팁 3: XML의 저장소 옵션 활용하기: 직렬화된, 압축된, 또는 분리된 테이블 공간
다음 샘플 데이터를 XML 데이터의 저장소 옵션으로 생각해보자. 테이블에는 관계형 데이터와 XML
데이터가 있다.
Listing 1. 샘플 테이블과 XML 및 관계형 데이터
create table product(pid bigint, name varchar(20), brand varchar(35),
category integer, price decimal, description XML); |
이 테이블 정의로 테이블의 XML 데이터와 관계형 데이터는 같은 테이블 공간에 기본으로 저장된다.
이들은 같은 페이지 크기를 사용하고 같은 버퍼 풀에서 버퍼된다는 뜻이다. 테이블 공간 내에서 관계형
데이터는 DAT 객체에 저장되고 XML 데이터는 XDA 객체에 저장된다. LOB 같은 XML 문서가
테이블의 데이터 페이지 상의 단일 열에 들어가기엔 너무 클 수 있기 때문이다. 이 기본 레이아웃으로
대다수의 애플리케이션 시나리오에서 좋은 성능을 얻을 수 있다.
성능 분석을 통해 XML 데이터에 크기가 큰 페이지가 필요하고 관계형 데이터나 인덱스에 크기가 작은
페이지가 필요하다면 이를 위해 분리된 테이블 공간을 사용할 수 있다. 테이블을 정의할 때 "긴"
데이터를 분리된 테이블 공간에 다른 페이지 크기로 명령할 수 있다. 긴 데이터에는 LOB와 XML
데이터가 있다.
다음은 두 가지 버퍼 풀과 두 가지 테이블 공간을 정의하는데 각각 4KB와 32KB 페이지를 나눈
예다(테이블 공간은 항상 버퍼 풀과 그에 맞는 페이지 크기를 요구한다). 테이블
"product"
는 테이블 공간
"relData"
에 4KB 페이지로 부여된다. 이 때 모든 행은 테이블 공간
"xmldata"
의 32KB 페이지에 저장되는 XML 행
"description"
만 제외하고 그 테이블 공간에 저장된다.
Listing 2. 분리된 테이블 공간과 버퍼 풀의 XML과 관계형 데이터
create bufferpool bp4k pagesize 4k;
create bufferpool bp32k pagesize 32k;
create tablespace relData
pagesize 4K
managed by automatic storage
bufferpool bp4k;
create tablespace xmlData
pagesize 32K
managed by automatic storage
bufferpool bp32k;
create table product(pid bigint, name varchar(20), brand varchar(35),
category integer, price decimal, description XML)
in relData
long in xmlData; |
DB2 9에서 기본 테이블 공간은 DB2 V8와 다르다. 명확히 지정되지 않는 한 새 테이블 공간은
큰 열 ID와 함께 DMS로 만들어진다. 즉 4KB의 테이블 공간은 V8에서의 64GB 대신에
2TB로 늘어나고 32KB 페이지는 512GB 대신 16TB가 된다. 또한 페이지 당 255열이라는
한계가 없어져, 32KB 페이지에 2335열까지 가능하다. 그러므로 페이지 당 열의 한계 자체는 더
이상 관계형 데이터에서 작은 페이지를 사용하는 이유가 못 된다.
DB2 9.5에서도 XML 데이터를 "직렬화되고" 압축되게 저장할 수 있다. 몇 가지 또는 모든
XML 문서가 DAT 객체의 기본 테이블 페이지 해당 열에 맞을 정도로 작다면 관계형 열에 직렬화될
수 있다. 이렇게 되면 더 직접적으로 XML 데이터에 접근할 수 있고 XDA 객체에 다시 접근할
필요가 없어진다. XML 행의 어떤 문서가 직렬화되기엔 아직 너무 크다면 원래대로 XDA 객체에
"병렬화되어(outlined)" 저장된다. 직렬화된 문서는 어떤 영역의 인덱스 엔트리도 요구하지 않으므로 영역 인덱스 크기를 대폭 줄일 수 있다. 직렬화된 문서는 Listing 3처럼 일반적인
DB2 열 압축을 사용해 압축될 수 있다.
Listing 3. 직렬화되고 압축된 XML 저장소
create table product(pid bigint, name varchar(20), brand varchar(35),
category integer, price decimal,
description XML inline length 3000) compress yes;
|
이 예에서 XML 행은 옵션
"inline length 3000"
으로 정의된다. 즉 3000바이트나 그 이하로 저장될 수 있는 문서는 직렬화될 것이라는 뜻이다.
직렬화는 DB2에서 XML을 파싱한 후의 문서 크기에 관련이 있을 뿐 파일 시스템의 원 XML 문서의
크기와 관련이 있는 게 아니다. 직렬 길이는 테이블의 페이지 크기에서 다른 행 크기를 뺀 것보다
작아야 한다. 직렬화된 XML 데이터는 항상 테이블의 관계형 행과 같은 테이블 공간에 있어야 하고
다른 페이지 크기나 분리된 테이블 공간에 저장될 수 없다.
샘플 테이블이 옵션
"compress yes"
로 정의되므로 관계형 데이터와 직렬화된 XML 문서는 압축된다. 직렬화된 XML 데이터를
70~85퍼센트로 압축하는 것은 일반적이다. 다음 구문은
"product"
테이블의 압축율을 확인하는 데 쓸 수 있다.
Listing 4. 압축률을 확인하는 Admin 함수
select tabname,pages_saved_percent,bytes_saved_percent
from table(sysproc.admin_get_tab_compress_info('MYSCHEMA','PRODUCT','ESTIMATE')) as t
|
시스템이 CPU 바운드가 아닌 I/O 바운드라면 XML 데이터를 압축함으로써 성능을 확연히 높일 수
있다. 하지만 직렬화는 데이터 페이지의 열 크기를 많이 증가시켰음을 기억하자. 그러므로 이는 페이지
당 열의 수를 줄인다. 테이블의 관계형 행에만 접근하는 질의는 이제 직렬화가 아닌 훨씬 많아진
페이지 수를 읽어야 한다. 그래서 더 많은 I/O가 생기고 질의에 대한 성능은 떨어질 수 있다.
질의가 항상 XML 행을 건드린다면 문제될 것이 없다.
한 마디로, XML 데이터를 위한 분리된 테이블 공간을 고려할 때는 일반 상식을 사용하자. 적은
버퍼 풀과 테이블 공간, 그리고 특히 적은 페이지 크기는 더 쉽게 관리, 유지, 튜닝할 수
있는 더 간단한 물리적 데이터베이스 디자인을 가능케 한다. 성능적으로 큰 이점이 있는 게
아니라면 다중 페이지 크기를 사용하지 말자. 직렬화와 압축을 사용해 저장소 소비를 줄이고
I/O 성능을 높이자.
팁 4: XML 데이터의 신속한 벌크 삽입을 위해 DB2를 구성하는 방법
DB2 9는 파일 시스템에서 DB2 테이블로 이동하는 두 가지 옵션인 인서트와 임포트를 지원한다.
인서트와 임포트는 성능과 뷰의 튜닝 포인트에 있어 특성이 비슷하다. 임포트 유틸리티가 사실
연속적 삽입을 실행하는 것이기 때문이다.
DB2 9.5부터 XML 데이터를 테이블로 옮기는 DB2 LOAD 유틸리티도 사용할 수 있다.
LOAD 유틸리티의 주 장점은 데이터가 기록되지 않는다거나 성능 향상을 위해 병렬처리가 자동으로
사용된다는 점 등이 XML이나 관계형 데이터에나 똑같이 적용된다는 것이다. DB2는 CPU 수와
테이블 공간 컨테이너에 기반을 두고 병렬처리의 기본 단계를 결정한다. 또한 LOAD 명령 구문에서
매개변수 CPU_PARALLELISM and DISK_PARALLELISM으로 CPU와 I/O
병렬처리를 설정할 수 있다.
애플리케이션이 동시 삽입 스레드를 통한 벌크 삽입을 수행하든 임포트나 로드를 사용하든 다음 성능
지침이 적용된다.
-
주요 필수조건으로 큰 페이지 크기에는 DMS 테이블 공간을 사용해야 한다(
팁 2
를 보자).
-
타겟 테이블의 어떤 인덱스도 정의하지 않았더라도 DB2의 pureXML 저장소 메커니즘은
효율적인 XML 저장소 접근을 위해 영역이라 불리는 것과 경로 인덱스를 명확히 유지한다.
-
다중 사용자가 정의된 XML 인덱스를 테이블에서 사용하려면 벌크 삽입 전에 정의하는 것이 벌크
삽입 이후에 만드는 것보다 더 낫다. 삽입 과정에서 각 XML 문서는 단
한 번만
처리돼
모든
XML 인덱스를 위한 인덱스 엔트리를 생성할 것이다. 하지만 다중 "인덱스 만들기"문을
내보내면 XML 행의 모든 문서는 몇 번씩 처리될 수 있다.
-
파일 시스템에서 DB2 테이블로 작은 XML 파일을 아주 많이 보내야 한다면 파일 시스템
캐싱이 불가능한 특정 파일 시스템에서 더 나은 성능을 보여준다. 각 파일이 단 한 번만 읽히므로 캐싱이 필요하지 않다. AIX에서 이 파일 시스템을 -o cio 옵션으로 마운트하게
되면 이점이 크다.
삽입과 임포트 운영에서 다음 지침을 고려하자.
-
"ALTER TABLE
<tablename>
APPEND ON"은 테이블에 모드를 붙일 수 있다. 새 데이터는 기존 페이지에서 빈 공간을
찾기보다는 테이블 끝에 붙는다. 이렇게 되면 벌크 삽입의 런타임 성능이 향상된다.
-
로그 버퍼 크기(LOGBUFSZ)와 로그 파일 크기(LOGFILSIZ) 증가는 삽입 성능을
높인다. 이는 특히 XML 삽입에서 중요한데 대체로 열 당 데이터 부피가 관계형 데이터보다
훨씬 크기 때문이다. 로그를 위해 신속한 I/O 디바이스가 필요하다.
-
"ALTER TABLE
<tablename>
ACTIVATE NOT LOGGED INITIALLY" (NLI)를 사용하면 로그할 필요가
없다. 하지만 구문이 실패하면 테이블은 접근 불가능하다고 표시되고 제거돼야 함에 주의하자. 이는
가끔 생산 시스템에서 거대 벌크 삽입을 위한 NLI를 막지만 빈 테이블의 초기에는 유용할 수
있다. NLI가 타겟 테이블로 동시에 삽입/임포트하는 것을 막기 때문에 병렬처리가 NLI보다
더 높은 성능을 낼 수 있음에 주의하자.
-
임포트를 사용하면 COMMITCOUNT 매개변수를 위한 작은 값은 성능에 방해가 될 수 있다.
매 100개의 열이나 그 이상을 처리하는 것이 각 열을 처리하는 것보다 더 나은 성능을 보여줄
것이다. 또한 COMMITCOUNT 매개변수를 제거하여 DB2가 가능한 한 많이 처리하게 할
수 있다.
-
다중 CPU와 디스크를 더 잘 활용하기 위해 다중 임포트 명령을 동시에 실행할 수 있다. 각
임포트가 자신의 데이터베이스 연결에서 작동하고 "ALLOW WRITE ACCESS" 절을
사용해 테이블 잠김을 피하도록 하자. 임포트를 동시에 실행하기 위해 분리된 입력 파일(DEL
파일)이 필요하진 않다. 임포트 명령은 "SKIPCOUNT
m
ROWCOUNT
n
"을 지정해 입력 파일에서
m
+1에서
m+n
열을 읽을 수 있게 하기 때문에 각 임포트는 같은 입력 파일의 다른 절을 읽을 수 있다.
삽입 성능 지침에 관해 더 알고 싶으면 "Tips for improving INSERT performance in DB2 Universal Database"이라는 글을 읽어보자. [
참고자료
참조]
한 마디로, 전통적인 삽입과 로깅 성능 튜닝은 XML 삽입과 임포트에 좋다. ALLOW
WRITE ACCESS 절을 각 임포트 명령에 추가한다면 병렬 임포트 세션을 실행할 수 있다.
DB2 9.5에서는 임포트 대신 로드를 사용하자.
팁 5: XML 성능 검사를 위해 새 스냅샷 모니터 요소 사용하기
페이지 크기를 다르게 적용하는 것의 이점이나 XML 성능의 차별화된 측면을 조사하다보면, 아마 관계형
데이터에 DB2 스냅샷 모니터를 사용하고 싶을 것이다. DB2 9은 관계형 데이터와 인덱스를 위해
기존 카운터에 적합한 XML 데이터를 위한 새 버퍼 풀 스냅샷 모니터 요소를 제공한다는 것을 찾게 될
것이다. 관계형 데이터와 인덱스는 테이블 공간의 분리된 저장소 객체에 저장되므로 분리된 읽기 및 쓰기
카운터를 갖는다. DB2 9의 pureXML 저장소는 XML 데이터를 위한 새 저장소 객체로
XDA라는, 자체 버퍼 풀 카운터를 갖고 있다.
아래 예는 스냅샵 모니터 출력의 스니펫이다. 데이터, 인덱스, XDA라는 세 가지 다른 저장소 객체를
위한 다양한 스냅샷 모니터 요소를 볼 수 있다. 이는 관계형 데이터와는 다른 XML의 버퍼링과 I/O
활동을 모니터, 분석할 수 있게 해 준다. XML 인덱스를 갖는 활동은 기존 인덱스 카운터에
포함된다. 새 XDA 카운터의 해석은 관련 관계형 카운터와 같다. 예를 들어 XDA 물리적 읽기 대
XDA 논리적 읽기의 낮은 비율은 XML 데이터의 높은 버퍼풀 히트율을 나타내는데, 이는 좋은
징조다. 버퍼 풀 스냅샷 모니터 요소에 관한 더 자세한 내용은 DB2 문서를 참조하라.
Listing 5. 데이터, 인덱스, XDA 저장소 객체를 위한 모니터 출력
Buffer pool data logical reads = 221759
Buffer pool data physical reads = 48580
Buffer pool temporary data logical reads = 10730
Buffer pool temporary data physical reads = 0
Buffer pool data writes = 6
Asynchronous pool data page reads = 0
Asynchronous pool data page writes = 6
Buffer pool index logical reads = 8340915
Buffer pool index physical reads = 54517
Buffer pool temporary index logical reads = 0
Buffer pool temporary index physical reads = 0
Buffer pool index writes = 0
Asynchronous pool index page reads = 0
Asynchronous pool index page writes = 0
Buffer pool xda logical reads = 2533633
Buffer pool xda physical reads = 189056
Buffer pool temporary xda logical reads = 374243
Buffer pool temporary xda physical reads = 0
Buffer pool xda writes
= 0
Asynchronous pool xda page reads = 97728
Asynchronous pool xda page writes = 0
Asynchronous data read requests = 0
Asynchronous index read requests = 0
Asynchronous xda read requests = 83528
|
한 마디로, 스냅샷 모니터 출력의 새 XDA 카운터는 XML 활동을 반영한다. 이는 버퍼 풀,
I/O, XML 데이터의 임시 공간 사용을 이해하는 데 유용하다.
팁 6: XML 스키마 유효성 오버헤드를 조심하라
XML 스키마는 XML 문서의 세트에서 허용하는 만큼 구조, 요소와 속성, 이들의 데이터 유형, 값
범위 등을 정의할 수 있다. DB2는 XML 스키마에 대해 XML 문서를 검증할 수
있다(선택적으로). 만약 검증할 문서를 선택하려면 데이터를 삽입하는 시점에 하는 것이 일반적이다.
이는 두 가지 목적을 위해 필요하다. 먼저 유효성은 데이터베이스에 삽입되는 데이터가 스키마 정의와
호환된다는 것으로, 예를 들어 테이블에 들어가는 "가치없는 데이터"를 막는다. 두 번째로 스키마
유효성은 스키마에서 각 XML 요소와 속성으로 유형 주석을 추가하고 이들 유형은 DB2 XML
저장소에 남는다. 예를 들어 XML 스키마가 부서 테이블의 직원 ID(
팁 1
에서 봤다)가 정수고 문서가 스키마에 대해 유효하다고 정의하면 DB2는 각 문서에서 직원 ID가 유형
xs:integer임을 기억한다. 직원 ID에서 열 비교를 수행하고자 한다면 질의 런타임에서 유형
오류로 실패할 것이다.
XML 스키마 유효성은 XML 파싱 과정에 수행되는 선택적 활동이다. 성능 조사를 통해 일반적으로
스키마 유효성이 [링크]가 가능할 때 XML 파싱은 CPU에 따라 다르다는 것이 밝혀졌다. 이
오버헤드는 XML 문서의 구조와 크기에 따라 매우 다르고 특히 사용된 XML 스키마의 크기와 복잡성에
따라 다르다. 예를 들어 적당히 복잡한 스키마와 스키마 유효성으로 CPU 소비가 50% 높아짐을
발견할 수 있다. XML 삽입이 I/O에 깊이 연관된 것이 아니라면 증가하는 CPU 소비는 일반적으로
삽입 효율이 떨어진다는 것을 뜻한다.
애플리케이션에서 XML 질의와 XML 스키마 호환의 유형 확인을 좀 더 엄격하게 해야 하는지 여부를
결정하자. 예를 들어 XML 문서가 데이터베이스에 저장되기 전에 이를 받고 검증하고 처리하는
애플리케이션 서버를 사용한다면 문서는 DB2에서 다시 검증될 필요가 없을 것이다. 이미 유효하다는
것을 알기 때문이다. 또는 데이터베이스가 신뢰하는 애플리케이션에서 XML 문서를 받는다면, 특히
제어하는 문서를 받는다면 XML 데이터는 항상 유효하다는 것을 알게 된다. 이 경우 삽입 성능을 높일
수 있다는 이점을 위해 스키마 유효성을 피하라. 하지만 DB2 데이터베이스가 신뢰하지 않는 소스에서
XML 데이터를 받는다면 DB2 수준의 스키마 호환을 확인하고 이를 위해 추가로 CPU 사이클을
소비해야 한다.
한 마디로, 고성능 삽입이 꼭 필요하지 않으면 DB2에서 스키마 유효성을 수행하지 않는다.
팁 7: XPath식에서 완벽히 한정된 경로를 가능한 많이 사용하기
테이블에 다음과 같은 XML 행이 있어 다음 구조의 "customerinfo" 문서를 관리한다고 가정하자.
Listing 6: 샘플 XML 문서
<customerinfo Cid="1004">
<name>Matt Foreman</name>
<addr country="Canada">
<street>1596 Baseline</street>
<city>Toronto</city>
<state>Ontario</state>
<pcode>M3Z-5H9</pcode>
</addr>
<phone type="work">905-555-4789</phone>
<phone type="home">416-555-3376</phone> |
고객의 전화번호나 살고 있는 도시를 찾아보고자 한다면 XQuery를 사용하든 SQL/XML을 사용하든
이 데이터를 갖는 몇 가지 가능한 경로식이 있다.
/customerinfo/phone
와
//phone
모두 전화번호를 받을 수 있다. 이와 마찬가지로
/customerinfo/addr/city
와
/customerinfo/*/city
는 도시 정보를 반환한다. *나
//
를 사용하는 것이 최고의 성능을 위해 완전하게 지정된 전체 경로가 좋다. DB2가 문서의 관련 없는 부분을
뛰어넘어 원하는 요소만 직접 찾을 수 있기 때문이다.
즉 문서 어디에 원하는 요소가 있는지 안다면 완벽히 지정된 경로에 정보를 제공한다.
/customerinfo/phone
대신
//phone
을 요청한다면 문서에 있는 전화 요소를 묻는 것이다. 이를 위해 DB2는 문서의
"addr"
하위 트리를 전체적으로 검색해 문서 안에서
phone
요소를 찾는다. 이렇게 하면 오버헤드를 피할 수 있다.
*와
//
가 원하지 않거나 예측하지 못한 질의 결과를 가져올 수도 있음을 기억하자. 예를 들어 아래처럼 어떤
"customerinfo" 문서가 "assistant" 정보도 포함한다고 하자. 경로
//phone
은 고객 정보
와
점원 전화번호를 구분하지 않고 반환할 것이다. 질의 결과에서 이것을 알지 못하면 점원의 전화번호를
고객의 전화번호로 잘못 처리할 수 있다.
Listing 7. 문서에서 다중 수준의 전화와 이름 요소
<customerinfo Cid="1004">
<name>Matt Foreman</name>
<addr country="Canada">
<street>1596 Baseline</street>
<city>Toronto</city>
<state>Ontario</state>
<pcode>M3Z-5H9</pcode>
</addr>
<phone type="work">905-555-4789</phone>
<phone type="home">416-555-3376</phone>
<assistant>
<name>Peter Smith</name>
<phone type="home">416-555-3426</phone>
</assistant>
</customerinfo>
|
한 마디로, 경로식의 *와 //를 피하고 가능하면 완벽히 지정된 경로를 대신 사용하자.
팁 8: 린(lean) XML 인덱스를 정의하고 모든 것을 인덱스하는 것 피하기
질의가 가끔 고객 이름으로 "customerinfo" 문서를 검색한다고 가정하자. 고객 이름 요소의
인덱스는 이런 질문의 성능을 크게 향상시킬 수 있다. 다음 예를 보자.
Listing 8. 고객 이름으로 검색하는 것을 지원하는 인덱스
create table customer(info XML);
create index custname1 on customer(info)
generate key using xmlpattern '/customerinfo/name' as sql varchar(20);
create index custname2 on customer(info)
generate key using xmlpattern '//name' as sql varchar(20);
select * from customer
where xmlexists('$i/customerinfo[name = "Matt Foreman"]' passing info as $i);
|
위에서 정의된 인덱스 모두는 고객 이름 상의 XMLEXISTS 술어를 평가할 수 있다. 하지만 인덱스
custname2
는 인덱스
custname1
보다 확연히 클 수 있다. custname2는 고객 이름뿐 아니라 점원 이름을 위한 인덱스 엔트리도
가지고 있기 때문이다. 이는 XML 패턴
//name
이 문서의 이름 요소와 맞기 때문이다. 하지만 점원 이름으로 절대 검색하지 않는다면 이들을 인덱스할
필요가 없다.
읽기 동작에 있어 인덱스
custname1
은 더 작기 때문에 잠재적으로 성능이 더 좋다. 삽입 업데이트나 삭제 동작에서 인덱스
custname1
은 고객 이름만을 위해 인덱스 유지 오버헤드에 손해를 입히는데 인덱스
custname2
는 고객
과
점원 이름을 위해 인덱스 유지를 요구한다. 최적의 삽입/업데이트/삭제 성능을 원하고 점원 이름 기반의
인덱스된 접근이 필요하지 않다면 추가 비용을 감수하면서 이를 실행하고 싶지 않을 것이다.
"모든 것을 인덱스하기"라는 다음의
heavyIndex
를 또한 고려하자. 여기에는 모든 텍스트 노드(예를 들어 각 XML 행의 각 XML 문서에서 각 리프
요소 값)의 인덱스 엔트리가 포함된다. 이런 인덱스는 삽입/업데이트/삭제 운영을 유지하는 데 비용이
많이 들고 많은 저장소 공간을 소비하므로 대체로 권고하지 않는다. 유일한 예외는 쓰기 활동이 많지 않고
질의 워크로드를 예측할 수 없는 애플리케이션으로 더 한정된 인덱스가 정의하기 힘들다.
create index heavyIndex on customer(info)
generate key using xmlpattern '//text()' as sql varchar(20);
|
한 마디로, XML 인덱스를 정의할 때 가능한 정확해야 하고 가능하면 *와 //를 피해야 한다.
팁 9: XMLQUERY 대신 XMLEXISTS에 문서 필터링 술어 놓기
다음 테이블과 데이터를 고려하자.
create table customer(info XML);
|
테이블 2. 고객 테이블에서 데이터의 세 열
<customerinfo>
<name>Matt Foreman</name>
<phone>905-555-4789</phone>
</customerinfo>
|
|
<customerinfo>
<name>Peter Jones</name>
<phone>905-123-9065</phone>
</customerinfo>
|
|
<customerinfo>
<name>Mary Poppins</name>
<phone>905-890-0763</phone>
</customerinfo>
|
|
이 주어진 테이블에서 "905-555-4789"라는 전화번호를 가진 고객 이름을 반환하고자 한다고
가정하자. 다음 질의를 쓰고 싶을 것이다.
select xmlquery('$i/customerinfo[phone = "905-555-4789"]/name' passing info as "i")
from customer;
|
하지만 이 질의는 적합한 것이 아니다. 여기에는 몇 가지 이유가 있다.
-
테이블에 열이 있으므로 많은 열을 가진 다음의 결과 세트를 반환한다. 이는 SQL 문에
where
절이 없어 열을 제거할 수 없기 때문이다.
|
<name>Matt Foreman</name>
|
3개 레코드가 선택된다.
-
술어를 맞추지 않은 테이블의 각 열에서 빈 XML 시퀀스를 가진 열이 반환된다. 이는
XMLQUERY 함수의 XQuery식이 한 번에 한 열(문서)에 적용되고 결과 세트에서 열을
절대 제거하지 않고
값
만 수정하기 때문이다. 이 XQuery에 의해 생산된 값은 술어가 참이면 고객 이름 요소거나
아니면 빈 시퀀스다. 이들 빈 열은 의미론적으로 맞고(SQL/XML 표준에 의거해) 질의가
방법대로 써졌으면 반환돼야 한다.
-
이 질의의 성능은 그리 좋지 않다. 먼저
/customerinfo/phone
에 존재하는 인덱스는 질의가 열을 제거할 수 없기 때문에 사용될 수 없다. 두 번째로 많은 빈
열을 반환하면 질의가 너무 느려진다.
성능 문제를 해결
하고
원하는 결과를 얻으려면 고객 이름을 추출하는 용도로만
select
절의 XMLQUERY 함수를 사용해야 하고
where
절에서 XMLEXITSTS 술어에 열을 제거해야 하는 검색 상태를 옮겨야 한다. 이렇게 하면 인덱스
사용과 열 필터링이 가능하고 빈 결과 열의 오버헤드를 피할 수 있다. 다음 방식으로 질의를 쓴다.
select xmlquery('$i/customerinfo/name' passing info as "i")
from customer
where xmlexists('$i/customerinfo/phone = "905-555-4789"' passing info as "i")
|
| <name>Matt Foreman</name> |
1개 레코드가 선택된다.
한 마디로, XMLQUERY 함수의 술어는 각 XML 값 내에서만 적용되므로 절대 열을 제거하지
않는다. 문서 필터링과 열 필터링 술어는 XMLEXISTS 함수로 가야 한다.
팁 10: 대괄호 [ ]를 사용해 XMLEXISTS에서 Boolean 술어 피하기
일반적 오류는 XMLEXISTS 함수에서 대괄호 없이 이전 질의를 쓰는 것이다.
select xmlquery('$i/customerinfo/name' passing info as "i")
from customer
where xmlexists('$i/customerinfo[phone = "905-555-4789"] and
$i/customerinfo[name = "Matt Foreman"]'
passing info as "i")
|
이는 다음 결과를 낼 것이다.
| <name>Matt Foreman</name> | | <name>Peter Jones</name> | | <name>Mary Poppins</name> |
3개 레코드가 선택된다.
XMLEXISTS 술어의 식이 작성되므로 XMLEXISTS는 항상 참(true)으로 나타난다.
그러므로 어떤 열도 제거되지 않는다. 이는 주어진 열에서 XQuery식 안에서 빈 시퀀스를 반환할
때만 XMLEXISTS 술어가 거짓이라고 평가하기 때문이다. 하지만 대괄호 없는 XQuery 질의는
항상 Boolean 값을 반환하고 빈 시퀀스는 절대 반환하지 않는 Boolean식이다.
XMLEXISTS는 값의
존재
를 확인하고 값이 Boolean 값 "거짓"이라도 값이 존재할 때 참이라 평가한다는 것을 기억하자.
이는 표현하고자 한 것이 아닐지라도 SQL/XML 기준에 따르면 올바른 행위다.
다시 언급하지만
phone
상의 인덱스는 어떤 열도 제거되지 않기 때문에 사용할 수 없고 실제 원했던 것보다 훨씬 많은 열을
받을 것이다. 또한 이 질의에서처럼 두 개나 그 이상의 술어를 사용할 때 같은 실수를 저지르지 않도록
조심하자.
Listing 9. XMLEXISTS에서 두 술어의 잘못된 사용
select xmlquery('$i/customerinfo/name' passing info as "i")
from customer
where xmlexists('$i/customerinfo[phone = "905-555-4789"] and
$i/customerinfo[name = "Matt Foreman"]'
passing info as "i")
|
이 질의는 대괄호를 사용하는데 무엇이 문제일까? XQuery식은 "
exp1
과
exp2
" 폼이기 때문에 여전히 Boolean 식이다. 이 질의를 작성해 열을 필터하고 인덱스 사용에 맞게
하는 올바른 방법은 다음과 같다.
Listing 10. 열을 필터하고 인덱스 사용을 가능케 하는 올바른 질의
select xmlquery('$i/customerinfo/name' passing info as "i")
from customer
where xmlexists('$i/customerinfo[phone = "905-555-4789" and name = "Matt Foreman"]'
passing info as "i")
|
한 마디로, XMLEXISTS의 Boolean 술어를 사용하지 말라. "and"와 "or"를
포함해 술어를 대괄호 안에 넣자.
팁 11: RUNSTATS를 사용해 XML 데이터와 인덱스의 통계 모으기
RUNSTATS 유틸리티는 XML 데이터와 XML 인덱스 상의 통계를 모으기 위해 확장되어 왔다.
DB2의 비용 기반 옵티마이저는 이 통계들을 사용해 XQUery와 SQL/XML 질의를 위한 효율적인
실행 계획을 생성한다. 그러므로 관계형 데이터에서 사용했던 것처럼 RUNSTATS를 계속 사용하자.
테이블이 관계형과 XML 데이터를 가지고 있고 관계형 통계만을 새로 고치고 싶다면 RUNSTATS를
새 절 "EXCLUDING XML COLUMNS"와 함께 실행할 수 있다. 이 절이 없다면 관계형과
XML 데이터를 위한 통계를 항상 취합하는 것이 기본이자 선호하는 행위다.
관계형 데이터와 XML 데이터를 위해 샘플링을 하여 RUNSTATS를 실행하는 시간을 줄일 수 있다.
대형 데이터 세트에서 데이터의 10퍼센트에서 통계는(또는 이보다 적은) 여전히 전체를 대변한다. 어떤
샘플링 퍼센트를 선택하든 runstats를 통해 열을 샘플링하거나(
Bernoulli sampling
) 페이지(
system sampling
)를 샘플링할 수 있다. 열 수준 샘플링은 모든 데이터 페이지를 읽지만 각 페이지의 열 %만 고려하기
때문에 이에 해당하는 XDA 페이지의 부분집합만 고려한다. 페이지 수준의 샘플링은 I/O를 확연히
줄이는데 데이터 페이지의 %만 읽기 때문이다. 그러므로 페이지 샘플링은 테이블이 XML뿐 아니라
적절한 양의 관계형 데이터도 가지고 있을 때 매우 향상된 성능을 보여줄 수 있다. 하지만 열 수준
샘플링은 관계형 데이터 값이 높게 밀집될 때보다 정확한 통계를 생산할 것이다.
여기 몇 가지 예가 있다. 첫 번째 runstats 명령은 테이블 고객과 샘플링 없는 이들의 인덱스를
위해 가장 광범위하고 자세한 통계를 모은다. 실행 시간이 허용된다면 이 방법이 가장 이상적이다. 두
번째 명령은 같은 통계를 모으지만 페이지의 10%만 모은다. 많은 경우 첫 번째 명령과 비슷한
수준으로 통계가 정확한 옵티마이저를 제공하지만 결과를 더 빠르게 반환할 것이다. 세 번째 명령은
모든 열의 15%를 샘플링하고 분산 통계를 모으지 않으며 첫 번째와 두 번째 명령에서 하지 않았던
인덱스에 샘플링을 적용할 것이다.
Listing 11. RUNSTATS를 사용해 통계 모으기
runstats on table myschema.customer
with distribution on all columns and detailed indexes all;
runstats on table myschema.customer
with distribution on all columns and detailed indexes all tablesample system (10);
runstats on table myschema.customer
on all columns and sample detailed indexes all tablesample bernoulli (15);
|
한 마디로, DB2는 XML 통계가 있을 때 더 나은 실행 계획을 생성한다. 사용하던 방법대로
runstats를 사용하거나 샘플링과 함께 runstats를 사용해 실행 시간을 줄이자.
팁 12: XML처럼 관계형 데이터를 노출하기 위해 SQL/XML 발행 뷰 사용하기
SQL/XML 발행 함수는 관계형 데이터를 XML 포맷으로 변환해준다. 이는 뷰 정의에서
SQL/XML 발행 함수를 숨길 때 이점이 될 수 있으므로 애플리케이션이나 다른 질의는 발행 함수들
자체가 아닌 뷰에서 구성된 XML 문서를 간단히 선택할 수 있다.
Listing 12. 뷰에 감춰진 SQL/XML 발행 함수
create table unit( unitID char(8), name char(20), manager varchar(20));
create view UnitView(unitID, name, unitdoc) as
select unitID, name,
XMLELEMENT(NAME "Unit",
XMLELEMENT(NAME "ID", u,unitID),
XMLELEMENT(NAME "UnitName", u.name),
XMLELEMENT(NAME "Mgr", u.manager)
)
from unit u;
|
뷰 정의에 몇 가지 관계형 행을 포함했음에 유의하자. 이는 구체화된 뷰가 아닌 단순한 뷰이므로 실제
리던던시(redundancy)를 만들지 않는다. 관계형 행을 노출하는 것은 이 뷰를 효율적으로 질의하는 데 도움이 된다.
하나의 특정 유닛을 위해 XML 문서를 꺼내야 한다고 하자. 다음 세 가지 질의는 이를 수행할 수
있지만 이 중 세 번째 질의는 특히 더 나은 성능을 보이는 경향이 있다.
첫 두 질의에서 필터링 술어는 구성된 XML에서 표현된다. 하지만 XML 술어는 기본 관계형 행이나
인덱스에 적용될 수 없다. 그러므로 이들 질의는 모든 유닛에 XML을 구성하는 뷰가 필요하고 유닛
"WWPR" 중 하나를 선택한다. 이는 최선이 아니다.
차선의 성능이 될 것이다.
Listing 13. 차선 성능인 질의
select unitdoc
from UnitView
where xmlexists('$i/Unit[ID = "WWPR"]' passing unitdoc as "i");
for $u in db2-fn:xmlcolumn('UNITVIEW.UNITDOC')/UNIT
where $u/ID = "WWPR"
return $u;
|
세 번째 질의는 관계형 술어를 사용해 더 짧은 런타임에서의 결과에서, 특히 거대 데이터 세트에서
"WWPR"의 XML 문서만 구성한다. 이 질의는 좋은 성능을 보인다.
Listing 14. 좋은 성능을 보이는 질의
select unitdoc
from UnitView
where UnitID = "WWPR";
|
한 마디로, SQL/XML 발행 뷰의 관계형 행을 포함하고 뷰를 질의할 때 구성된 XML
상에서보다 이들 행에서 술어를 표현한다.
팁 13: 관계형 포맷에서 XML 데이터를 노출하기 위해 XMLTABLE 뷰 사용하기
XML 포맷에서 뷰를 만들어 관계형 데이터를 노출시키는 것이 유용할 수 있는 것처럼 뷰를 사용해
관계형 포맷에서 XML 데이터를 노출시키고 싶을 것이다.
팁 12
에서 언급했던 것과 비슷하지만, 반대로 주의해야 할 점이 있다. 표 포맷에서 XML 문서에서 값을
반환하는 데 쓰는 SQL/XML 함수 XMLTABLE이 있는 다음 예를 보자.
Listing 15. 표 포맷에서 XML 문서에서 반환되는 값
create table customer(info XML);
create view myview(CustomerID, Name, Zip, Info) as
SELECT T.*, info
FROM customer, XMLTABLE ('$c/customerinfo' passing info as "c"
COLUMNS
"CID" INTEGER PATH './@Cid',
"Name" VARCHAR(30) PATH './name',
"Zip" CHAR(12) PATH './addr/pcode' ) as T;
|
뷰 정의에 XML 행 정보를 포함시켜 이 뷰를 효과적으로 질의할 수 있도록 했다는 것에 주목하자.
주어진 우편번호로 고객 ID와 이름의 표 목록을 찾아오고자 한다고 하자. 다음 질의는 모두 이를
찾아올 수 있지만 두 번째 질의의 성능이 더 낫다. 첫 번째 질의에서 필터링 술어는 XMLTABLE
함수로 생성되는 CAHR 행 "ZIP"에서 표현된다. 하지만 관계형 술어는 기본 XML 행이나 그
인덱스에 적용될 수 없다. 그러므로 이 질의는
모든
고객의 열을 생성하는 뷰가 필요하고 우편번호 "95141"에 맞는 정보를 뽑아낸다. 이는 최선이
아니다. 두 번째 질의는 XML 술어를 사용해 "95141" 열만 생성되도록 하고 이는 특히 거대
데이터 세트에서 더 짧은 런타임을 가져온다.
Listing 16. XML 술어로 질의하기
-- may perform suboptimal:
select CustomerID, Name
from myview
where Zip = "95141";
-- will perform well:
select CustomerID, Name
from myView
where xmlexists('$i/customerinfo[addr/pcode = "95141"]' passing info as "i");
|
뷰가 정의된 기본 테이블이 XML 행뿐 아니라 인덱스와 관계형 행도 포함한다면 이 관계형 행들을 뷰
정의에 포함시켜야 한다. 뷰에 대한 질의가 관계형 행에 엄격히 제한된 술어를 가진다면 DB2는
관계형 인덱스를 사용해 관련 열을 작은 수로 필터한 후 최종 결과 세트를 반환하기 전에
XMLTABLE과 다른 남은 술어를 이 임시 결과에 적용한다.
한 마디로, 관계형 폼에 XML 데이터를 노출하는 XMLTABLE 뷰에 주의하라. 가능하다면 뷰
정의에 추가 행을 포함시켜 필터링 술어가 XMLTABLE 행 대신 이들 행에서 노출되게 할 수
있다.
팁 14: 짧은 질의나 OLTP 애플리케이션을 위해 매개변수 마커와 함께 SQL/XML 문 사용하기
아주 짧은 데이터베이스 질의는 가끔 너무 신속히 실행되어 이를 컴파일, 최적화하는 시간이 전체 응답
시간의 아주 일부분일 수 있다. 그러므로 한 번에 컴파일("준비")하고 각 실행의 술어 정수 값만
보내는 것이 유용하다. DB2 9 XQuery는 외부 매개변수를 지원하지 않지만 SQL/XML 함수
XMLQUERY, XMLTABLE, XMLEXISTS는 지원한다. 이들은 SQL 매개변수 마커를
변수로 끼워진 XQuery 식에 보낸다. 이는 질의가 짧고 반복적인 애플리케이션에 적합하다.
Listing 17. 변경하지 못하게 코드화된 술어 정수 값
for $c in db2-fn:xmlcolumn('CUSTOMER.INFO')/customer
where $c/phone = "905-555-4789"
return $c;
select info
from customer
where xmlexists('$i/customerinfo[phone = "905-555-4789"]'
passing info as "i")
|
Listing 18. 매개변수 마커와 함께 사용
select info
from customer
where xmlexists('$i/customerinfo[phone = $p]'
passing info as "i", cast(? as varchar(12)) as "p")
|
한 마디로, 짧은 질의와 OLTP 트랜잭션은 준비된 구문과 매개변수 마커이기에 더 빠르다.
XML에서는 SQL/XML로 SQL 스타일의 매개변수를 XQuery식으로 보내야 한다.
팁 15: XML 삽입 및 찾아오는 중 코드 페이지 변환 피하기
XML은 내외부적으로 인코드될 수 있으므로 DB2의 다른 데이터 유형과는 다르다. 내부적 인코딩은
XML 데이터의 인코딩이 데이터 자체에서 추론될 수 있다는 것을 뜻하고 외부적 인코딩은 외부 정보에서
인코딩을 추론한다는 것을 의미한다. XML 데이터와 DB2를 교환하는 데 쓰는 애플리케이션 변수의
데이터 유형은 인코딩이 어떻게 추론되는지 결정한다. 애플리케이션이 XML에 문자 유형 변수를 사용하면
애플리케이션 코드 페이지에서처럼 외부적으로 인코딩된 것이다. 바이너리 애플리케이션 데이터 유형을
사용하면 XML 데이터는 내부적으로 인코딩된 것이라 할 수 있다. 내부적 인코딩은 다음처럼 유니코드
BOM(바이트 오더 마크)이나 XML 문서 자체에서의 인코딩 선언에 의해 결정된다는 뜻이다.
<?xml version="1.0" encoding="UTF-8" ?>
성능 관점에서 코드 페이지 변환이 추가 CPU 주기를 소비하므로 가능한 한 이를 피하는 것이
목표다. 내부적으로 인코딩된 XML 데이터는 필요없는 코드 페이지 변환을 막을 수 있으므로
외부적으로 인코딩된 데이터보다 선호된다. 즉 애플리케이션에서 문자 유형보다 바이너리 데이터 유형을
사용해야 한다는 뜻이다. 예를 들어 CLI에서 SQLBindParameter()를 사용해 매개변수
마커를 입력 데이터 버퍼에 묶을 때 SQL_C_CHAR, SQL_C_DBCHAR나
SQL_C_WCHAR보다 SQL_C_BINARY 데이터 버퍼를 사용해야 한다. 자바 애플리케이션에서
XML 데이터를 삽입할 때 XML 데이터를 바이너리 스트림(setBinaryStream)으로 읽는
것은 열(setString)로 읽는 것보다 낫다. 비슷하게 자바 애플리케이션이 DB2에서 XML을
받고 이를 파일에 쓴다면 코드 페이지 변환은 XML이 바이너리가 아닌 데이터에 작성될 때 나타날
것이다.
DB2에서 XML 데이터를 애플리케이션에서 찾아올 때 이는 연속된다. 직렬화는 XML 파싱의 역
운영이다. 이는 DB2의 내부 XML 포맷을(분석된, 트리식 표현) 애플리케이션이 이해할 수 있는
원래 XML 포맷으로 변환하는 프로세스다. 대부분 DB2가 함축된 직렬화(implicit
serialization)를 수행하는 것이 좋다. 즉 SQL/XML문은 간단히 XML 유형 값을
다음 예에서 선택하고 DB2는 가능한 한 효율적으로 애플리케이션 변수에 직렬화를 수행한다는 뜻이다.
Listing 19. 함축적 직렬화로 질의하기
create table customer(info XML);
select info from customer where...;
select xmlquery('$i/customerinfo/name' passing info as "i")
from customer
where...;
|
애플리케이션이 대용량 XML 문서를 처리한다면 데이터 찾기에 LOB 로케이터를 사용하는 것이 좋을 수
있다. 이 때 LOB 유형, 특히 BLOB에 명시적 직렬화가 요구되는데 이는 CLOB 같은 문자
유형의 명시적 직렬화(explicit serialization)가 인코딩 이슈와 필요없는 코드 페이지
변환을 소개할 수 있기 때문이다. 명시적 직렬화는 XMLSERIALIZE 함수를 사용한다.
select XMLSERIALIZE(info as BLOB(1M)) from customer where...;
|
한 마디로, XML을 DB2와 교환하는 데 애플리케이션의 바이너리 유형을 사용하면 불필요한 코드
페이지 변환을 피할 수 있다. 인코딩 이슈를 조심하고 의심스럽다면 DB2 9 문서의 자세한
지시를 따르기 바란다.
요약
DB2에서 최대 XML 성능을 달성하려면 자동 저장소나 자가 튜닝 메모리 관리 같은 DB2의 자동화
기능을 사용하는 것이 좋다. 이는 많은 애플리케이션에 깔끔하고 훌륭한 성능을 제공한다. 또한 DBA가
필요할 때 성능 튜닝에 더 많은 시간을 쏟을 수 있게 해 준다. 모든 전통적인 DB2 성능의 지혜는
여전히 XML에 적용되고 아래 열거한 것처럼 developerWorks의 다양한 글들에서 다루고
있다.
그 중 이 글에서 다루는 15개 팁이 XML에 특화된 성능 향상 측면에서 도움이 될 것이다. XML
애플리케이션의 성능을 향상시켜야 한다면 15개 팁을 전부 적용할 필요 없이 상황에 맞게 한두 개만
적용하면 된다. 예를 들어 시스템이 부적절한 테이블 공간 구성 때문에 I/O 바운드에 묶여있다면
불필요한 코드 페이지 변환을 줄이는 것은 도움이 안 된다. 비슷하게 더 나은 질의 실행 계획을 위해
실제로 runstats를 실행해야 한다면 SDQL/XML 매개변수 마커를 사용하는 것은 질의 성능에
도움이 안 된다. 간단히 이 글의 팁은 성능 이슈를
피할 수 있게
하지만
관찰된
성능 문제를 고치는 데는 먼저 기본 문제와 병목의 이유를 아는 것이 중요하다. visual
explain, db2exfmt 및 스냅샷 모니터 같은 DB2의 표준 진단 도구는 관계형 데이터에서처럼
XML 성능을 조사하는 데 사용할 수 있다.
감사의 글
이 글을 검토하고 도움을 준 Cindy Saracco, Irina Kogan, Henrik
Loeser, Nikolaj Richers, Marcus Roy에게 감사한다.
참고자료 교육
-
XML 관련 글
-
Fraser McArthur의
Best practices for tuning DB2 UDB v8.1 and its databases
를 통해 DB2 UDB 데이터베이스와 그 애플리케이션에 필요한 최적의 성능을 갖자.
-
Scott Hayes의
Top 10 performance tips
를 통독하여 유닉스, 윈도우, OS/2 환경에서 DB2 UDB의 전자상거래 OLTP 애플리케이션을
위한 성능 팁을 배워보자.
-
Artis Walker의
DB2 and AIX tuning essentials for DB2 performance
를 읽고 AIX 내에서 눈에 띄는 DB2 성능 향상을 위해 할 수 있는 일이 무엇인지 배워보자.
-
Yongli An과 Peter Shum의
DB2 Tuning Tips for OLTP Applications
를 통해 OLTP(온라인 트랜잭션 프로세싱) 유형의 성능 벤치마크(TPC-C, TPC-W,
Trade2 등)에서 배운 것을 토대로 한 몇 가지 DB2 튜닝 팁에 초점을 맞추자. 데이터베이스
애플리케이션의 성능은 많은 요소에 의해 영향을 받을 수 있다. 이 글은 DB2의 구성 측면에
집중한다.
-
Bill Wilkins은
Tips for improving INSERT performance in DB2 Universal Databaset
에서 삽입했을 때 정확히 어떤 일이 발생하는지 설명하고 대안적으로 잠금, 인덱스 유지, 제약 관리
같은 삽입 성능에 영향을 주는 이슈를 검사한다.
-
Allen Lee의
Improve database performance on file system containers in IBM DB2 UDB V8.2 using Concurrent I/O on AIX
를 읽고 AIX 상의 다양한 애플리케이션 I/O 모델을 이해하고 DB2가 CIO 기능을 사용하는
방법에 대해 알아보자.
-
Larry Pay의
RUNSTATS in DB2 UDB Version 8.2
를 읽고 기존 옵션과 새 옵션 모두에서 최적의 데이터베이스 성능을 얻기 위해 RUNSTATS를
사용하는 방법을 이해하자.
-
Xiaomei Wang, Wini Mark, Ken Lau와 Raul F. Chong의
DB2 UDB OLTP tuning illustrated with a Java program
은 IBM DB2 UDB 데이터베이스 서버를 모니터, 튜닝하는 기술을 단계별로 설명한다.
-
리눅스, 유닉스, 윈도우에서의 DB2에 대한 developerWorks 참고자료 페이지
를 방문해 관련 글과 튜티리얼 및 다른 참고자료를 통해 DB2 스킬을 넓히자.
-
커뮤니티의 무료 DB2 익스프레스 에디션인
DB2 Express-C
에 대해 배워보자.
제품 및 기술 얻기
토론
필자소개  | |  | Dr. Nicola는 IBM 실리콘 밸리 랩에서 XML 데이터베이스 성능을 담당하는 수석
기술자다. 전문 분야는 XQuery, SQL/XML을 포함, DB2의 XML 성능의 모든 것과
DB2의 모든 고유 XML 기능이다. Dr. Nicola는 DB2 XML 개발팀뿐만 아니라 XML을
사용하는 고객 및 비즈니스 파트너들과 직접 일하며 디자인, 구현, XML 솔루션 최적화를 지원하고
있다. IBM에 입사하기 전 인포믹스 소프트웨어에서 데이터웨어하우징 성능 관련 업무를 담당했다.
또한 분산형 및 복제형 데이터베이스에 대한 연구와 업계 프로젝트를 4년간 진행했다. 1999년 독일의
Technical University of Aachen에서 컴퓨터 공학 박사학위를 받았다 |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|