티스토리 뷰

목차


    데이터베이스에 쿼리를 전송했을 때, 원하는 결과가 나오기까지의 시간이 유독 오래 걸려 답답했던 경험이 있으신가요? 문법에 오류가 없고 결과가 정상적으로 출력된다 하더라도, 수백만 건의 데이터를 다루는 실무 환경에서는 '얼마나 빠르고 효율적으로 데이터를 가져오는가'가 시스템의 명운을 좌우합니다. 이때 내가 작성한 SQL 문장이 데이터베이스 내부에서 정확히 어떤 경로를 거쳐 실행되는지 그 내비게이션 경로를 텍스트나 트리(Tree) 형태로 상세하게 보여주는 지도가 바로 '실행 계획(Execution Plan)'입니다. DBMS의 두뇌 역할을 하는 옵티마이저(Optimizer)는 쿼리를 접수하면 수많은 경우의 수를 계산하여 가장 비용(Cost)이 적게 드는 최적의 경로를 선택하는데, 개발자는 이 실행 계획을 읽고 분석함으로써 옵티마이저가 엉뚱한 길로 돌아가고 있지는 않은지, 인덱스는 제대로 타고 있는지, 불필요한 풀 스캔(Full Scan)이 발생하고 있지는 않은지 정확하게 진단할 수 있습니다. 오늘은 쿼리 성능 튜닝의 가장 기본이자 핵심인 SQL 실행 계획을 어떻게 읽고 해석해야 하는지, 초보자의 눈높이에 맞추어 그 기초적인 판독 순서와 반드시 알아야 할 핵심 키워드들을 아주 상세하게 파헤쳐 드립니다. 실행 계획을 지배하는 자가 데이터베이스를 지배합니다.

     

     

     

     

     

    1. 실행 계획의 기본: 읽는 순서의 대원칙 (안에서 밖으로, 위에서 아래로)

    실행 계획을 처음 마주하면 들여쓰기(Indentation)가 복잡하게 얽혀 있어 어디서부터 시선을 두어야 할지 막막해집니다. 실행 계획을 해독하는 절대적인 대원칙은 딱 두 가지입니다. 첫째, 들여쓰기가 가장 깊은 곳(가장 안쪽)부터 먼저 읽습니다. 둘째, 들여쓰기 깊이가 완전히 동일한 항목들이 나란히 있다면 가장 위에 있는 것부터 먼저 읽습니다. 즉, 트리 구조의 가장 깊은 하단에서부터 데이터를 실제로 퍼 올린 다음, 부모 노드(위쪽)로 그 데이터를 전달하며 최종적으로 사용자에게 도달하는 흐름(Bottom-Up)을 머릿속에 그리셔야 합니다. 이 순서만 정확히 파악해도 여러 개의 테이블이 조인(JOIN)될 때 어떤 테이블을 먼저 읽었는지(Driving Table) 명확하게 추적할 수 있으며, 튜닝의 시작점을 아주 쉽게 찾아낼 수 있습니다.

     

    요약: 실행 계획은 들여쓰기가 가장 깊은 안쪽(오른쪽) 항목부터 시작하여, 동일한 깊이일 경우 위에서 아래의 순서로 데이터를 퍼 올려 해석합니다.

     

    옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드
    옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드

     

     

    2. 데이터 접근 방식 확인: TABLE ACCESS FULL vs INDEX SCAN

    순서를 파악했다면 각 단계에서 데이터를 '어떻게' 가져왔는지 표기된 Operation(동작) 항목을 유심히 살펴봐야 합니다. 가장 흔하게 발견되는 두 가지 방식이 있습니다. 첫 번째는 'TABLE ACCESS FULL(테이블 풀 스캔)'로, 인덱스를 타지 못하고 테이블의 처음부터 끝까지 모든 블록을 무식하게 다 뒤져서 데이터를 찾았다는 뜻입니다. 데이터가 수백만 건이라면 엄청난 성능 저하를 유발합니다. 두 번째는 'INDEX RANGE SCAN' 또는 'INDEX UNIQUE SCAN' 등으로, 잘 만들어진 목차(Index)를 활용하여 필요한 데이터가 있는 위치로 단숨에 찾아갔다는 의미입니다. 만약 조회 조건(WHERE 절)에 분명히 인덱스가 걸린 컬럼을 사용했는데도 실행 계획에 풀 스캔이 뜬다면, 형 변환이 일어났거나 인덱스 컬럼을 가공해서 옵티마이저가 인덱스를 무시해 버린 것은 아닌지 즉각적인 의심을 해보아야 합니다.

     

    요약: 대용량 테이블에서 TABLE ACCESS FULL이 발생하면 성능 병목의 원인이 되므로, 적절한 인덱스를 활용한 INDEX SCAN으로 유도해야 합니다.

     

     

     

     

    3. 조인(JOIN) 메커니즘 해석: NL Join, Hash Join, Sort Merge Join

    두 개 이상의 테이블을 연결할 때 옵티마이저가 어떤 조인 알고리즘을 선택했는지도 실행 계획의 핵심 관전 포인트입니다. 'NESTED LOOPS(중첩 루프)'는 가장 전통적인 방식으로, 먼저 읽은 테이블(선행 테이블)의 데이터 한 건 한 건마다 다음 테이블(후행 테이블)을 반복해서 찾아가 조인하는 방식입니다. 인덱스가 잘 구성되어 있고 추출 건수가 적을 때 매우 유리합니다. 반면 'HASH JOIN(해시 조인)'은 조인 컬럼을 기준으로 메모리 영역에 해시 맵을 만들어 단번에 데이터를 매칭하는 방식으로, 대용량 데이터를 조인할 때 NL 조인보다 압도적으로 빠릅니다. 만약 수십만 건을 조인하는데 실행 계획에 NESTED LOOPS가 잡혀 있다면 쿼리 응답 시간이 한세월 걸릴 수 있으므로, 옵티마이저 힌트(Hint)를 주어 HASH JOIN으로 강제 변경해 주는 튜닝 기법을 고려해야 합니다.

     

    요약: 소량 데이터 추출에는 NESTED LOOPS 조인이 유리하며, 대용량 테이블 간의 조인 시에는 메모리를 활용하는 HASH JOIN으로 동작하는지 확인해야 합니다.

     

    옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드
    옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드

     

     

    4. 성능 저하의 주범 찾기: Cost(비용)와 Rows(예상 건수) 지표 분석

    실행 계획의 각 스텝 우측에는 옵티마이저가 내부적으로 계산한 예상 수치들이 표기됩니다. 여기서 가장 중요하게 봐야 할 것은 'Cost(비용)'와 'Rows(예상 추출 건수)'입니다. Cost는 이 작업을 수행하는 데 시스템 자원(CPU, 메모리, 디스크 I/O)이 상대적으로 얼마나 소모될지를 나타내는 논리적인 수치입니다. 만약 특정 스텝에서 Cost가 이전 단계에 비해 비정상적으로 훅 뛰어오른다면, 바로 그 지점이 쿼리를 느리게 만드는 치명적인 병목 구간(Bottleneck)일 확률이 높습니다. 또한 Rows는 해당 스텝을 거치면 몇 건의 데이터가 출력될지 옵티마이저가 통계 정보를 바탕으로 예측한 건수입니다. 만약 실제 테이블에는 데이터가 100건밖에 없는데 실행 계획 상의 Rows가 100만 건으로 잡혀 있다면 통계 정보가 심각하게 틀어졌다는 뜻이므로, 옵티마이저가 엉터리 실행 계획을 세우지 않도록 통계 정보를 최신화(Analyze) 해주는 조치가 필요합니다.

     

    요약: 실행 계획 우측의 Cost(비용)가 급격히 상승하는 구간이 성능 병목 지점이며, Rows(예상 건수)가 실제와 크게 다르면 통계 정보 갱신이 필요합니다.

     

     

     

     

    5. 실전 팁: 툴(GUI)을 활용한 시각적 실행 계획 분석

    과거에는 터미널 환경에서 `EXPLAIN PLAN FOR` 명령어를 타이핑하여 텍스트로 실행 계획을 힘들게 읽었지만, 현대의 개발 환경에서는 DBeaver, SQL Developer, DataGrip 같은 훌륭한 클라이언트 툴을 적극적으로 활용하는 것이 훨씬 현명합니다. 이 툴들은 쿼리를 블록 지정한 뒤 단축키(예: DBeaver의 경우 Ctrl+Shift+E)만 누르면, 복잡한 들여쓰기를 직관적인 트리(Tree) 구조나 시각적인 다이어그램 노드 형태로 변환하여 예쁘게 보여줍니다. 어떤 테이블이 병렬로 처리되고 있고, 어디서 병목이 걸리는지 붉은색 막대그래프로 한눈에 파악할 수 있어 초보자들의 튜닝 진입 장벽을 극적으로 낮춰줍니다. 실행 계획을 읽는 것은 한 번에 완벽해지지 않습니다. 평소 자신이 작성한 쿼리에 대해 습관적으로 실행 계획 탭을 열어보는 훈련을 반복하다 보면, 어느새 데이터베이스 아키텍처를 꿰뚫어 보는 훌륭한 시야를 갖추게 될 것입니다.

     

    요약: 텍스트 명령어 대신 DBeaver 등 최신 GUI 툴이 제공하는 시각적 트리 형태의 실행 계획 보기 기능을 활용하면 병목 구간을 훨씬 직관적으로 파악할 수 있습니다.

     

    옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드
    옵티마이저의 마음을 읽다: SQL 실행 계획 보는 순서와 인덱스 스캔 완벽 가이드