자동화 툴로 채울 수 없는 DB 성능관리 2% 1. 자동화 툴을 이용한 성능관리의 한계

2007. 2. 6. 09:35 IT 및 개발/MS-SQL & T-SQL
데이터베이스 성능관리는 단순한 지식으로 해결할 수 있는 문제가 아니다. 자동화된 툴에서 보여지는 많은 지표의 도움으로 종합적인 판단해야 한다.

이번 글의 출발점은 바로 이 자동화 툴이다. 여기서는 기존의 접근 방법과는 조금 다르게 데이터베이스 활용과 성능관리에 대한 이야기를 시작해 보자.

오늘날에는 거의 모든 기업 활동이 정보 시스템을 통해 이루어지고 있다. 간단한 계산 작업에서부터 복잡한 업무 프로세스까지 정보 시스템을 이용하지 않고는 처리할 수 없는 환경이라고 해도 과언이 아니다. 이같은 상황에서 한 기업의 업무 프로세스를 정보 시스템화한다는 것은 단순히 업무를 전산화한다는 의미뿐만 아니라 회사의 다양한 업무를 표준화, 자동화, 고품질화 해 효율성을 높이고 나아가 회사의 경쟁력을 높이는 원동력이 되는 것이다.

그러나 현재 대부분의 전산 시스템의 상황은 어떠한가? 정보 시스템을 통해 고부가가치를 창출하려는 노력보다는 처음부터 계획성 없이 구축해 놓은 전산 시스템을 유지보수하거나 신뢰할 수 없는 데이터에 대한 보정작업(data cleansing) 등 고급 인력들이 단순 노무자(?)로 전락하고 있는 것은 아닌지 유감스럽다.

기업의 복잡하고 다양한 업무를 전산화하려면 무엇보다 이 수많은 유무형의 작업들을 데이터화하고 이를 관리하는 데이터베이스가 필수적이다. 현재의 모든 전산 시스템이 데이터베이스를 근간으로 만들어지고 있는 것도 이 때문이다. 하지만 실제로 업무 전산화를 담당하는 개발자들은 수많은 개발 언어를 익히는데 치중하는 나머지 업무를 데이터화하는 능력과 데이터베이스를 활용하는 능력을 기르는데 소홀할 수밖에 없다. 데이터베이스를 관리하는 DBA(DataBase Administrator)들은 업무 중 많은 시간을 데이터베이스 운영의 문제점과 이를 해결(trouble shooting)하는 데 허비하고 있다.

필자는 컨설턴트로 활동하면서 이런 데이터베이스 운영상의 문제점들을 수없이 많이 보아왔다. 본래 이러한 문제들의 근본원인을 이해하는 것도 중요하지만, 관리자 입장에서 보면 먼저 이런 문제점들을 발견하고 해결하는 것이 더 중요하다. 실제로 최근 많이 출시돼 있는 데이터베이스 모니터링 자동화 툴도 이런 취지에서 선보인 것들이다.

그러나 이런 툴이 있음에도 불구하고 데이터베이스 운영에 많은 문제점들이 끊임없이 나타나는 것은 데이터베이스란 하나의 물리적인 소프트웨어에 지나지 않지만 실제로는 업무의 논리적인 데이터적 활용이라는 관점에서 사용되기 때문이다.

앞으로 3회에 걸쳐 데이터베이스 운영의 문제점 중 가장 민감하다고 할 수 있는 ‘성능(performance)’ 관리에 대해, 자동화된 툴을 이용한 관리에서 더 논리적이고 데이터베이스 활용 측면의 이야기를 살펴 본다. 데이터베이스는 현재 시중에서 가장 널리 사용되는 오라클 데이터베이스를 예제로 하겠다.

성능관리의 성패를 가르는 요인들
요즘 대부분의 기업에서 이용하고 있는 데이터베이스는 RDBMS(Relational Database Management System)라 부르는 RDBMS이다. RDBMS를 근간으로 구축하는 시스템에서는 실제 대부분의 시스템에서 성능상 문제를 나타내는 원인의 많은 부분이 RDBMS 관련 문제 요소들이다.

4~5년 전만 해도 시스템 구성이나 인프라(infra), 리소스(resource) 부족으로 인한 문제가 많았으나, 하드웨어 기술이 빠르게 발전하고 상대적으로 비용 부담이 줄어들면서 쉽게 하드웨어를 증설할 수 있게 됐다. 실제로 대부분의 기업들이 하드웨어와 메모리 증설이라는 처방을 통해 어느 정도 시스템 부하와 자원부족 문제를 해결해 온 것도 사실이다.

그러나 이러한 시스템 문제들의 내부를 살펴보면 사실상 CPU, 메모리, 디스크, 네트워크 등 하드웨어 리소스 부족으로 인한 문제 상황은 거의 찾아볼 수 없다. 가끔씩 2티어(tier), 3티어 환경 또는 웹 환경에서 네트워크 구성 문제나 네트워크 속도 문제로 인한 문제가 발생하기도 하지만, 필자의 경험에 비추어 보면 거의 절대적으로 시스템 성능의 영향을 미치는 부분은 바로 RDBMS에 맞도록 시스템 설계(데이터모델링/DB Design)가 이루어졌는지 여부와 RDBMS에 적합한 개발기법으로 개발이 이루어졌는 지가 문제의 핵심이었다.

즉 운영업무의 특성과 발생 데이터량 그리고 트랜잭션 부하를 고려해 RDBMS의 특성과 장단점을 데이터베이스 설계에 반영하고, RDBMS 옵티마이저(optimizer) 개념을 이해하고 응용 프로그램을 개발해야 하며, 성능에 가장 직접적인 영향을 미치는 I/O(Input/Output) 성능을 개선하겠다는 설계자와 개발자들의 노력이 매우 중요하다 할 수 있다. 일반적으로 데이터 베이스 성능에 영향을 미치는 부분은 다음과 같이 구분할 수 있다.

◆ 물리적인 데이터베이스 설계 측면
◆ 개발 응용 프로그램 SQL의 품질측면
◆ 최적화/클러스터링 전략 측면
◆ 시스템 자원 사용 측면

물리적인 데이터베이스 설계 측면
RDBMS에 대해 정확하게 이해하지 못한 상황에서 DB 오브젝트 디자인을 해 대부분의 데이터베이스 파라미터를 기본(default)으로 설정해 관리하거나 조정을 하려고 해도 할 수 없는 경우, 혹은 스토리지 설계가 잘못된 경우가 대부분이다.

이런 요소들은 문제 위험요소에 치명적이지는 않지만 RDBMS 성능을 높이기 위해서는 RDBMS의 원리와 DB 스토리지 알고리즘을 이해해 DB 오브젝트 디자인에 반영해야 한다. 특히 PCTFREE, PCTUSED, FREELISTS, INITRANS, INITIAL, NEXT EXTENTS 설계 등 데이터 발생과 액세스(access) 등에 영향을 미칠 수 있는 물리적인 요소들에 대해 적절한 설계가 이루어져야 한다.

또한 대용량의 데이터 환경과 데이터 변환, 마이그레이션(migration) 등의 환경에서는 파티션(partition) 전략이 중요하다. 파티션의 장단점과 그 원리를 정확히 파악하고 현재 데이터 환경에 적용시킬 수 있는 능력이야 말로 관리자가 갖춰야 할 부분 중의 하나다. 많은 자동화 툴들이 이런 기능을 단순히 처리(operation)해 주는 편리한 기능을 많이 제공한다. 보통 단순 작업을 진정한 실력으로 착각하는 경우가 많은데, 적용 능력과 활용 기술 등은 자동화 툴로 대체할 수 있는 것이 아니다.

개발 응용 프로그램 SQL의 품질측면
RDBMS에 맞는 개발 기법은 유일하게 RDBMS가 이해할 수 있는 언어인 SQL을 RDBMS 옵티마이저가 최적화할 수 있게 만드는 것이다. 즉 실력있는 프로그래머는 옵티마이저이며 이것이 최적의 수행속도를 낼 수 있도록 개발자와 설계자가 적절하게 전략적인 최적화 팩터(factor)를 구축해야 한다. 즉 개발 응용 SQL 역시 옵티마이저가 알기 쉽게 집합적인 고급 SQL을 구사하여야 한다.

대부분의 성능상의 문제는 이런 집합적이고 전략적인 SQL을 구사하지 못해 발생한다. SQL은 사용하기는 편리하지만 잘못 사용하면 1초면 가능할 일을 1시간이 넘도록 수행하는 경우도 있다. 즉 SQL은 요구와 결과 밖에 없는 비절차형 언어이기 때문에 누구나 쉽게 배우고 사용할 수 있지만, 최종 사용자 컴퓨팅(End User Computing) 환경이 아닌 패키지 시스템이나 개발시스템에서는 가장 효과적인 고급 SQL을 사용할 수 있는 능력이 요구된다. 이런 능력은 단순하게 툴을 이용하거나 편협한 지식으로 해결될 수 있는 것이 아니다. 원리를 이해하려는 많은 노력으로만 가능하다.

최적화/클러스터링 전략 측면
필자의 경험에 따르면, 대부분의 데이터베이스 인덱스 전략은 한 마디로 주먹구구식으로 만들어 놓고 쓰는 것이 대부분이다. RDBMS를 정확히 이해하지 못하고 사용하는 습관이 만들어 낸 매우 위험한 현상이다. 실제로 개별 SQL의 속도를 빠르게 하기 위해 즉흥적으로 인덱스를 추가하면 오히려 큰 오류를 범하게 된다.

Trace 결과에 근거해 각종 SQL에 어떠한 조건들이 사용될 것인지 그리고 어떤 테이블 간에 연결 조인이 이루어지는지 등을 파악해 모두를 만족할 최소의 역할별 인덱스 또는 클러스터 전략을 수립해야 한다. 상황에 따라 개별 개발자들이 수행속도 향상을 위해 케이스(Case) 별로 인덱스를 추가하기 시작하면 무수히 많은 인덱스가 늘어나 시스템 부하만 가중시키고 정작 의도하는 엑세스 효율은 얻지 못하는 경우가 대부분이다.

반면 아무리 데이터량이 많아도 최적의 데이터베이스 디자인과 효율적이고 전략적인 인덱스 전략 등 최적화 전략이 갖춰져 있으면 전혀 걱정할 필요가 없다. 사실상 대부분은 반복 업무는 온라인 업무이며 이를 처리할 때 수십 수백만 건을 모두 읽어서 처리해야 한다면 이는 업무 정의가 잘못되거나 적절한 데이터베이스 디자인이 되어 있지 않은 것이다.

현재 시스템에 성능상의 문제가 있을 경우 적절하지 못한 데이터 모델링에 기인하는 경우가 많은데 실제로 이런 문제를 데이터 모델링의 문제라고 생각하기 보다는, 단지 개발자들의 능력이 부족하기 때문이라고 속단한다.

그러나 핵심은 원하는 데이터를 가장 효율적으로 최소의 비용으로 처리할 수 있도록 RDBMS에 최적화된 전략을 갖추고 있는가 하는 점이다. 사용자들이 쉽고 빠르게 시스템에 접근해 원하는 바를 얻고, 효율을 높이기 위해서는 전체적인 SQL 액세스 패스를 분석해 최적화 전략을 수립하는 것이 중요한 것이다.

시스템 자원 사용 측면
시스템 자원사용에 관한 부분도 성능상에 문제를 야기할 수 있는 중요한 측면이다. 과거에는 CPU, 메모리, 디스크 등 시스템 자원이 부족해 생기는 부분이 많았으나 현재는 가용한(available) 자원을 제대로 활용하고 있지 못하는데 대부분의 원인이 있다.

하드웨어 자원이 부족하기 때문이 아니라 비효율적인 애플리케이션으로부터 비롯된다는 사실은, 앞서 설명한 설계문제, 최적화 전략 부재, 비효율적인 애플리케이션 등이 복합적으로 작용해 현재 시스템의 자원 사용에 대한 부적절한 스케일링(scaling)이 뒤따를 수 있는 문제가 발생한 것이다. 데이터 중복이 심하고 비정규화로 인해 RDBMS의 기본 개념인 ‘관계’의 데이터 정합(integrity)가 깨지면, 당연히 데이터 액세스 작업이 비효율적으로 되고 불필요한 인덱스들이 양산될 수밖에 없다.

RDBMS 원리에 충실한 DB 디자인과 옵티마이저 원리에 충실한 응용 프로그램 개발, 앞을 내다보는 최적화 전략 등이 성능관리의 성패를 가름하는 것이다.

DB 관리 툴의 활용법과 한계
DBA들은 데이터베이스 운영의 책임을 지고 이를 안정적으로 운영할 의무가 있지만, 실제로 데이터베이스에 문제가 발견되면 그 원인을 찾기 힘든 경우가 많다. 이 때문에 자동화 툴을 이용해 성능상의 문제점을 파악하고 해결하려 한다. 데이터베이스는 매우 복잡한 소프트웨어이고 운영상 많은 지식과 경험 등이 필요해, 관리하는 사람들마다 서로 다를 수밖에 없다.

따라서 아무리 자동화된 툴을 이용한다고 해도 그 지식과 경험의 차이에 따라 툴을 이용하는 수준은 천차만별이다. <화면 1>은 거의 모든 DBA들이 매일같이 확인하는 데이터베이스 세션(Session)에 관한 모니터링 관리화면이다.


<그림 1> 자동화된 툴의 세션 모니터링 화면

이런 종류의 관리화면은 거의 모든 데이터베이스 툴이 제공하는 것으로 데이터베이스 관리자가 데이터베이스에 액세스되고 있는 많은 세션 정보를 편리하게 볼 수 있도록 지원한다. 여기서는 모든 세션에 대한 정보는 물론 그 세션들이 현재 데이터베이스에 어떤 요구를 하고 있는지도 확인할 수 있다.

이런 요구들 가운데에는 데이터베이스에 많은 부하를 주거나 속도 저하를 일으키는 SQL들도 발견할 수 있다. 이를 편리하게 찾아 주는 것이 자동화된 툴의 장점이기도 하다. 실제로 문제가 되는 세션의 SQL을 찾고 싶다면(실제로 이런 요구가 빈번히 발생하게 된다) 대부분의 DBA나 개발자들은 개인적인 경험과 지식을 동원해 문제가 되는 SQL들을 찾아낸다.

Physical I/O, Logical I/O, Lock Wait 등을 보고 툴에서 보내주는 수치가 상대적으로 크거나 또 나름대로의 판단기준을 가지고 속도 저하의 가능성이 있는 SQL이라 판단할 수 있는 것이다. 예를 들어 <리스트 1>과 같은 SQL이 문제 요소로 부각됐다고 가정해 보자.

<리스트 1> 자동화된 툴에서 발취한 SQL문 예제
/* XXXXXXXXXXX */
select e.disp_cate_id, f.cart_no, f.cart_seq, f.mem_no, f.mall_no, f.cate_id,
    f.cart_goods_cnt, f.ord_path,
a.sale_goods_no,a.sale_goods_seq, a.goods_pln_seq,
c.goods_nm,    -- 상품명
decode(pln_gb,    -- 기획구분에 따라
'0',b.goods_sale_amt,    -- 상설판매상품
'1',a.three_sale_price,    -- 공동구매상품
'3',a.one_sale_price)    -- 이벤트기획상품
sale_price, -- 판매가
' ',    -- URL
a.sale_goods_no||a.sale_goods_seq||a.goods_pln_seq prod_code, -- 상품코드
c.mker_nm,    -- 제조사
z2.goods_img_c     -- 이미지(세)
from t_goodspln a, t_salegoods b, t_goods c,
    t_cocont d, t_display e, t_cart f,
    (select goods_no,goods_img_path goods_img_c
from t_goodsimg
    where goods_img_gb = '04') z2
where f.mem_no = :1
  and f.cart_gb = :2
  and f.goods_no = a.sale_goods_no
  and f.cate_id = e.disp_cate_id
  and f.sale_goods_seq = a.sale_goods_seq
  and f.goods_pln_seq = a.goods_pln_seq
  and a.sale_goods_no = b.goods_no  
  and a.sale_goods_seq = b.sale_goods_seq
  and b.goods_no = c.goods_no
  and b.co_cont_id = d.co_cont_id
  and a.sale_goods_no = e.goods_no
  and a.sale_goods_seq = e.sale_goods_seq
  and a.goods_pln_seq = e.goods_pln_seq
  and c.goods_no = z2.goods_no(+)
  and d.cont_end_dt > to_char(sysdate,'yyyymmdd')
  and d.cont_st_dt <= to_char(sysdate,'yyyymmdd') -- 유효한 계약
  and d.app_gb = '1'    -- 업체계약승인여부 = 숭인(1)
  and a.goods_reg_app_gb = '1'    -- 상품등록승인구분 = 승인(1)
  and b.cur_sale_gb = '1'    -- 현재판매구분 = 판매(1)
  and e.disp_target = '1'    -- 전시구분 = 상품
  and e.disp_yn = '1'    -- 전시상태 = 전시
and e.app_gb = '1'    -- 전시승인구분 = 승인(1)

이때 가장 중요한 것은 어떤 부분이 문제가 되는지를 정확히 판단해 As-Is와 To-Be에 대한 요구 가능성이 충족될 수 있도록 최적화된 튜닝을 하는 것이다. 이를 위해서는 여러 가지 수많은 팩터들 중에서 가장 기본적으로 실행 계획과 트레이스(trace) 정보를 보고 이해할 수 있어야 한다.

<리스트 2> 자동화된 툴에서 추출한 트레이스 정보 예제
call
count
cpu
elapsed
disk
query
current
rows
-------
-------
------
-------
------
--------
-------
---------------
Parse
1
0.00
0.00
0
0
0
0
Execute
1
0.00
0.00
0
0
0
0
Fetch
1
27.01
113.68
184594
1996701
0
1
-------
------
-------
------
-------
-------
-------
-------------
Total
3
27.01
113.68
184594
1996701
0
1

Misses in library cache during parse: 0
Optimizer goal: Choose
Parsing user id: XXX
Rows Row Source Operation
----- --------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID T_CART (cr=1996701 r=184594 w=0 time=113681900 us)
106518 NESTED LOOPS (cr=1975955 r=172219 w=0 time=108118888 us)
85176 NESTED LOOPS (cr=1783923 r=170113 w=0 time=104990664 us)
95396 NESTED LOOPS (cr=1318382 r=131520 w=0 time=81215311 us)
87097 NESTED LOOPS OUTER (cr=866280 r=104936 w=0 time=64587792 us)
87097 NESTED LOOPS (cr=524102 r=59175 w=0 time=33144088 us)
87276 NESTED LOOPS (cr=262453 r=29318 w=0 time=15840258 us)
514 TABLE ACCESS BY INDEX ROWID T_COCONT (cr=647 r=0 w=0 time=14612 us)
633 INDEX RANGE SCAN IX_COCONT_1 (cr=634 r=0 w=0 time=7236 us)(IX_COCONT_1)
87276 TABLE ACCESS BY INDEX ROWID T_SALEGOODS (cr=261806 r=29318 w=0 time=15734153 us)
130389 INDEX RANGE SCAN IX_SALEGOODS_2 (cr=131417 r=489 w=0 time=2169944 us)(IX_SALEGOODS_2)
87097 TABLE ACCESS BY INDEX ROWID T_GOODS (cr=261649 r=29857 w=0 time=16906365 us)
87097 TABLE ACCESS BY INDEX ROWID T_GOODS (cr=261649 r=29857 w=0 time=16906365 us)
80887 TABLE ACCESS BY INDEX ROWID T_GOODSIMG (cr=342178 r=45761 w=0 time=30957253 us)
80887 INDEX UNIQUE SCAN IX_GOODSIMG_PK (cr=261291 r=13643 w=0 time=10321114 us)(IX_GOODSIMG_PK)
95396 TABLE ACCESS BY INDEX ROWID T_GOODSPLN (cr=452102 r=26584 w=0 time=16106493 us)
95405 INDEX RANGE SCAN IX_GOODSPLN_PK (cr=356696 r=7990 w=0 time=5710096 us)(IX_GOODSPLN_PK)
85176 TABLE ACCESS BY INDEX ROWID T_DISPLAY (cr=465541 r=38593 w=0 time=23115538 us)
89346 INDEX RANGE SCAN IX_DISPLAY_PK (cr=376194 r=11357 w=0 time=9600217 us)(IX_DISPLAY_PK)
21341 INDEX RANGE SCAN IX_CART_4 (cr=192032 r=2106 w=0 time=2695905 us)(IX_CART_4)

<그림 2>와 같은 실행계획을 보고 현재 어떤 문제가 있고 어떻게 튜닝해야 할지 판단하는 것은 쉬운 일이 아니다. 단순히 SQL의 문법지식이나 인덱스 상태만으로 해결되는 것도 아니다. 데이터 상황과 시스템 상황, 인덱스 상황, 다른 SQL들의 액세스 패턴 등을 종합적으로 판단할 수 있어야 한다. 이런 종합적인 판단에 있어 툴은 많은 도움을 줄 수 있지만 ‘최종적인 판단’까지는 대신해 줄 수 없다. 이 부분은 사람만이 가능하다. <리스트 3>과 같은 또다른 사례를 살펴 보자.

<리스트 3> 게시판 테이블에서 날짜를 이용해 내용을 조회하는 SQL
SELECT B.*, BBS_GET_STATUS(b.owner_id, '001006363', '7') STATUS
FROM BBS B
WHERE B.INSERT_DATE <= SYSDATE
ORDER BY insert_date DESC,b.brd_id

<리스트 3>은 특정 게시판 테이블에서 날짜를 이용해 간단한 내용을 조회하는 SQL로, 요즘 대부분의 회사에서 많이 쓰이는 그룹웨어 시스템의 모든 화면에서 조회할 수 있는 SQL이다. BBS 테이블에 많은 데이터가 있지도 않고 특성상 단위 SQL이 수행되는 시간 또한 오래 걸리지 않으며, 언뜻 보기에도 별 문제가 없어 보인다. 어쩌면 실제로 별 문제가 없을 수도 있다.

그러나 <리스트 3>을 자세히 살펴 보면 owner_id 즉 로그인한 ID를 입력 값으로 해 BBS_GET_STATUS 함수를 통해 어떤 상태(status)를 리턴(return) 받는 부분이 있다. 만약 이 시스템을 이용하는 기업의 사용자가 매우 많고 빈번히 조회가 된다면 바인딩되는 owner_id 값의 종류가 기하급수적으로 늘어날 것이다.

이 때 동적으로(dynamic) 바뀌는 값을 데이터베이스에서 바인딩하지 않을 경우 동일한 SQL을 수천만번 파싱(parsing)해야 하는 경우가 생기게 된다. 따라서 단위 SQL은 빠르게 수행이 될 수 있으나 전체적인 데이터베이스의 성능은 시간이 갈수록 저하돼, 마치 시한폭탄과도 같아지게 된다.

만약 자동화된 툴에서 이런 SQL들을 전문적으로 필터링하는 기능이 있다면 이런 정형화된 경우에 한해서는 툴의 자동화 기능을 이용하는 것도 좋은 방법 중 하나가 될 것이다. 하지만 실제 업계에서는 데이터베이스 전문적인 툴이라 하더라도 데이터베이스 자체가 아닌 사용 관점에서 문제점을 발견하고 찾아내는 자동화된 툴은 본 적이 없다.

DBA는 물론 데이터베이스 전문가라고 하는 사람들도 자동화된 툴을 가지고 데이터베이스를 모니터링하는 수준에 불과한 경우도 많았으며, 데이터베이스 문제의 근본 원인을 제대로 파악하는 것이 쉽지 않은 작업임을 여러 차례 확인한 바 있다. 즉 모니터링 툴이 보여주는 물리적인 지표를 이용하면 데이터베이스 자체의 문제를 발견할 수는 있지만, 실질적인 근본 원인과 그 해결책을 찾는 것은 또 다른 이야기가 되는 것이다.


<화면 2> 일반적인 자동화 툴의 데이터베이스 모니터 화면

<화면 2>는 많은 자동화 툴에서 보여주고 있는 데이터베이스 모니터 화면이다. 이런 화면에서 보여주고자 하는 것을 제대로 이해하는 것도 쉬운 일은 아니지만, 제대로 이해했다고 해도 그 원인을 해결하는 것은 더욱 어렵다.

예를 들어 SGA Memory Usage에서 Shared Pool의 사용량이 상대적으로 많다면 그 이유가 무엇인지 파악해서 해결할 수 있어야 하지만 대부분의 DBA들은 그 원인을 치유하지 못한 채로 Shared Pool Size만 늘려 해결하려고 한다. 그러나 실제 그 이유를 살펴보면 메모리 크기가 부족한 것인지 아니면 다이나믹 쿼리(dynamic query)에 의한 파싱이 불필요하게 많아서인지 등 여러 가지가 있을 수 있으며, 설사 원인을 파악했다고 해도 이를 어떻게 해결할 수 있는지 알지 못한다.

필자가 경험적으로 느낀 바에 따르면, 대부분의 원인은 데이터베이스 지향적 관점으로 프로그램으로 개발하지 않은, 즉 애플리케이션의 문제가 80~90%를 차지한다. 이 경우 메모리 크기를 늘리면 당장의 해결책은 되겠지만 시간이 흐를수록 더 큰 문제가 야기될 수밖에 없다. 따라서 근본 원인을 찾고 해결하는데 시간을 투자하는 것이 안정적으로 데이터베이스를 운영하는데 필수적이다.

한편 인덱스를 통한 쿼리가 많고 적음에 따라 시스템의 부하를 줄일 수 있는 팩터가 되기도 한다. <그림 5>는 Indexed Queries%를 보여주는데 이 화면으로 데이터베이스에 요구하는 SQL들이 인덱스를 많이 이용하는지 아니면 전체 스캔(Full Scan)을 많이 하는지를 한눈에 알 수 있다.

여기서 독자들에 대한 질문 한 가지! 이때 인덱스를 많이 타면 좋은 것일까? 다양한 답변이 나올 수 있을 것이다. 인덱스에 대해 깊이 파고 들었던 독자는 다양한 상황을 예로 들며 장황하게 설명할 수도 있겠고, 단순하게 ‘그렇다’라고 대답하는 독자도 있을 것이다. 하지만 대부분은 인덱스를 타면 빨라지니 이를 많이 타면 시스템 부하가 내려갈 것이라고 생각할 것이다. 데이터베이스 전문가라고 자부하는 사람들조차 이렇게 생각하고 있으니 일반적인 개발자나 DBA들은 그렇게 생각하는 것이 어쩌면 당연할 수도 있다.

그러나 이것이 항상 옳은 것은 아니다. 인덱스에 대한 기본 구조와 개념, 인덱스를 통한 스캔 방식과 전체 스캔 방식의 차이, 각 스캔 방식의 데이터 상황에 따른 반응 시간(Response Time), 실시간 용인지 데이터 웨어하우스 용인지 등 데이터베이스의 사용목적 등을 심도있게 연구해야 다양한 환경에 따라 목적에 맞는 사용방식을 찾을 수 있다.

이런 관점에서 보면 기본으로 제공되는 자동화된 툴들은 현재 시스템의 전체 윤곽을 잡는데 큰 도움이 되는 것은 분명하다. 그러나 이 툴을 이용하는 사람들이 단순히 툴을 다루는(operation) 오퍼레이터(operator)에 머무른다면 수많은 문제를 올바르게 해결할 수 없다.

앞서 설명했듯 문제점의 원인을 파악하다 보면 대부분은 효율적이지 못한 SQL 문장, 최적화 전략의 부재 등에서 시작된다. 어떤 경우 프로그램 하나가 전체적인 시스템의 성능을 좌지우지 하는 경우도 있다. 필자가 컨설팅했던 한 사이트는 운영시스템이 항상 90% 이상의 CPU 사용률을 기록하고 있었고 시스템의 부하가 너무 심해 CPU와 메모리를 최대로 탑재해서 더 이상 리소스 증설을 할 수 없는 상태였다.

리소스를 최대로 증설한 상태에서도 사용률이 90% 이상을 기록하니 운영 중에 많은 부작용이 나타났고 상품을 판매하는 회사에서 고객을 유치하는 프로모션을 해야 함에도 불구하고 시스템이 이를 지원하지 못해 걱정만 하고 있었다.

분석 결과 이 시스템은 근본원인을 치유하기 보다는 즉흥적인 조치로 인덱스 수가 무수히 늘어나 시간이 갈수록 더욱 더 심각한 상황에 이른 경우였다. 운영시간에 조작되는 프로그램은 많았고 특히 대부분의 사용자들이 빈번하게 사용하면서 시스템의 부하를 크게 높이는 프로그램 한두 개를 찾아 이에 대한 긴급 처방을 하자 CPU 사용률이 30% 정도로 떨어졌다. 튜닝 컨설팅이 끝날 무렵에는 시스템의 과다 탑재된 CPU의 활용 용도를 놓고 행복한 고민에 빠지게 됐다.

회사 측면에서 보면 프로그램 한두 개가 막대한 손해를 끼친 경우로, 옵티마이저의 기본원리를 알고 프로그램을 개발했더라면 그런 현상을 미연에 방지했을 것이다. 이런 것은 특정 툴 하나를 능숙하게 다루고 자동화된 어떤 작업을 잘해서 해결되는 것이 아님을 독자들은 이해하리라 생각한다.

참고로 데이터베이스의 성능관리에 있어서 애플리케이션의 중요성을 강조하는 이유는 앞서 설명했듯, 데이터베이스가 스탠드 얼론(stand alone)으로 운영되는 것이 아니라 사용자의 요구(request)에 의해 운영되는 것이고, 그 사용 자체가 비즈니스이기 때문에 활용적인 면에서 접근할 수밖에 없기 때문이다.


<화면 3> 속도 저하가 나타난 애플리케이션 화면


<화면 4> 전형적인 웹 애플리케이션 소스

<화면 3>과 <화면 4>는 RDBMS의 특성에 맞는 애플리케이션 개발 방법이 중요하며, 자동화된 툴의 한계가 명확하다는 것을 보여주는 대표적인 사례다. 이것은 모 유통회사에서 문제가 되었던 애플리케이션을 튜닝한 것으로, 대부분의 웹 애플리케이션 개발자가 접하게 되는 프로그램 화면과 프로그램 개발 소스다. 다양한 검색조건에 따라 SQL을 추가(append)해 조건에 맞는 답이 나올 수 있는 SQL을 만들어 데이터베이스에 요구하는 방식으로 짜여져 있다. 그러나 이러한 방식에는 다음과 같은 문제점이 있다.

[1] 다양한 검색조건에 의해 다른 SQL이 만들어짐에 따라 데이터베이스의 파싱 오버헤드(parsing overhead)가 증가할 수 있다.
[2] 여러 SQL에 의한 여러 액세스 패스로 인해 전략적인 클러스터링 팩터로 인한 성능저하 가능성이 있다.
[3] 파라미터가 데이터베이스에서 바인딩되지 않으므로 조건 값(Value)에 따라 매번 파싱을 해 파싱 오버헤드가 생길 수 있다.

이런 프로그램들을 과도하게 생성해 사용하면 클러스터링 팩터의 부재로 인해 성능이 저하되고, 파싱 오버헤드에 의해 시스템 리소스 사용이 증가돼 전체적으로 시스템 성능이 떨어지고 심하면 장애가 발생할 수도 있다. 이런 상황들을 모르는 시스템 담당자 또는 DBA들은 문제가 발생하면 원인을 찾기 힘들게 되고 시스템 증설이나 인덱스 추가 등 즉흥적인 대처에 급급해 시스템에 오히려 독이 되는 것이다. <화면 3>과 <화면 4>에서 보이는 SQL의 일부 트레이스 정보를 추출하면 <리스트 4>와 같다.

<리스트 4> <화면 3>의 일부 조건에 대한 트레이스 정보
SELECT /*+ ORDERED USE_NL( A B C D )*/
      COUNT(*) total_cnt
  FROM SC011M A
    , SC013M B
            , SC010C C
    , CM101C D
  WHERE B.MDLCD = A.MDLCD
    AND B.AVCLDT = '99991231'
    AND SUBSTR(A.PRDCLS, 1, 2) = C.PRDCLS_CD
    AND A.MAKR_CD = D.VEND_NO
AND A.MDL_KORNM LIKE '%냉장고%'
call
count
cpu
elapsed
disk
query
current
rows
--------
--------
---------
--------
-------
--------
--------
--------
Parse
2
0.04
0.05
0
0
2
0
Execute
2
0.00
0.17
0
0
0
0
Fetch
2
12.80
27.20
0
0
0
2
--------
--------
---------
--------
-------
-------
----------
--------
Total
6
12.84
27.43
0
0
2
2

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 21
Rows Row Source Operation
------- -------------------------------------------------
1 SORT AGGREGATE (cr=0 r=0 w=0 time=14316920 us)
8351 NESTED LOOPS (cr=0 r=0 w=0 time=14307069 us)
8351 NESTED LOOPS (cr=0 r=0 w=0 time=9451685 us)
8351 NESTED LOOPS (cr=0 r=0 w=0 time=4710553 us)
3561 REMOTE (cr=0 r=0 w=0 time=680595 us)
8351 REMOTE (cr=0 r=0 w=0 time=938957 us)
8351 REMOTE (cr=0 r=0 w=0 time=2001549 us)
8351 REMOTE (cr=0 r=0 w=0 time=1985918 us)

<리스트 4>는 <화면 3>의 화면에서 ‘상품명’의 조건으로만 조회했을 때의 SQL에 대한 트레이스 정보로, 전체적인 트레이스 정보를 각각 분석해 튜닝하는 방법보다 화면 단위로 전체적인 클러스터링 팩터를 선정해 튜닝하는 것이 효과적이다. 여기서는 화면 단위로 봤을 때 Mandatory 조건을 어떤 것으로 할 것이며 그에 따른 Optional 조건은 어떤 것으로 하고, 또 그에 따른 데이터 상황과 인덱스 전략 등을 수립해 종합적으로 판단해 <리스트 5>와 같이 SQL을 one-set으로 만드는 방법을 사용했다.

[1] 다양한 검색조건을 단일 SQL로 처리해 검색조건에 따른 파싱 오버헤드를 미연에 방지한다.
[2] 단일 SQL로 처리하면서 ‘상품품목’ 조건을 팝업창에서 선택하도록 하는 Mandatory 조건화하여 고정된 액세스 패스를 유지시켜 성능 향상을 유도한다.
[3] 모든 파라미터는 데이터베이스에서 바인딩할 수 있도록 PrepareStatement 문장을 이용해 실행한다. 이를 통해 조건값에 따른 파싱 오버헤드를 방지한다.
[4] 대부분의 프로그램이 이런 조합형의 SQL로 되어 있어 가능한 모든 SQL들을 One-SQL로 수정해야 한다.

이와 같이 튜닝을 하면 모든 조건검색을 안전하고 빠르게 수행할 수 있으며, 동시에 시스템 부하를 줄일 수 있다. RDBMS의 특성과 기본 개념, SQL 등의 올바른 사용법 등을 참조해 데이터베이스 지향적인 프로그램을 개발한 단적인 사례로, 앞서 설명한 여러 자동화된 툴에서 채울 수 없는 ‘2%’이기도 하다.

<리스트 5> <리스트 4>를 튜닝한 SQL
SELECT chk, model_code, mid_item, product_name, price, maker
  FROM (SELECT rownum rnum, chk, model_code, mid_item, product_name, price, maker
    FROM (SELECT /*+ DRIVING_SITE(a) ORDERED USE_NL( A B C D ) INDEX(A SC011M_IDX02) */
        ''                chk
        , A.MDLCD        model_code
        , C.PRDCLS_KORNM        mid_item
        ,     A.MDL_KORNM        product_name
        ,     B.BASE_AMT        price
        ,     D.VEND_NM        maker
      FROM SC011M A
          , SC013M B
          , SC010C C
              , CM101C D
      WHERE B.MDLCD = A.MDLCD
        AND B.AVCLDT = '99991231'
        AND SUBSTR(A.PRDCLS, 1, 2) = C.PRDCLS_CD
        AND A.MAKR_CD = D.VEND_NO
        AND A.MDLCD LIKE :s_iptModelCode||'%'
        AND A.PRDCLS = :s_prdcls
        AND A.MDL_KORNM LIKE :s_iptSrch||'%'
        AND C.PRDCLS_KORNM LIKE :s_iptItem||'%'
        AND D.VEND_NM LIKE :s_iptMaker||'%'
      ORDER BY A.MDLCD, C.PRDCLS_KORNM, A.MDL_KORNM, B.BASE_AMT
      )
    )
  WHERE rnum BETWEEN :s_start AND :s_end

지금까지 다양한 예제들을 통해 RDBMS의 특성을 이용한 성능 문제 해결 방안에 대해 살펴 봤다. 많은 관리자들이 자동화된 툴의 한계와 적절한 이용법을 익혀 데이터베이스의 문제점을 빨리 파악하고 이를 분석, 해결할 수 있는 데이터베이스 전문가로 성장하기를 기대한다. 다음 글부터는 실제 현장에서 자주 부딪히는 사례를 중심으로 각각의 해결 방법을 상세하게 알아 본다.

출처 : 마이크로소프트웨어 2005년 2월호