인덱스 스캔 튜닝 실습
인덱스 스캔 튜닝
개발자를 위한 오라클 SQL 튜닝 내용에서 참고한 내용입니다.
인덱스를 최대한 활용하여 원하는 결과 검색하기
소비자 테이블, 주문 테이블 생성
기본키, 외래키 생성(참조 무결성 제약 조건)
테이블 관계
두 테이블의 관계는 1:N 관계
한 명의 고객은 여러 건을 주문할 수 있고, 한 건도 주문하지 않을 수 있다.
한 개의 주문은 반드시 한 명의 고객을 가져야 한다.
통계정보 생성
인덱스와 테이블에 대한 통계 정보 생성
1. 테이블 풀 스캔 해보기
문제점
TB_CUST 테이블의 CUST_NM 컬럼이 변별력 있는 컬럼임에도 인덱스를 생성하지 않음
TB_ORD 테이블의 CUST_ID 컬럼이 변별력 있는 컬럼임에도 인덱스를 생성하지 않음
OLTP(Online Transaction Processing)환경에서 빈번하게 사용되는 SQL문이라면 반복적인 테이블 풀 스캔은 전체 시스템에 큰 부하를 주게 된다.
Execute Plan
TB_CUST 테이블 풀 스캔 -> TB_ORD 테이블 풀 스캔
TB_CUST 테이블과 TB_ORD 테이블을 해시 세미 조인을 한다.
해시 세미 조인은 옵티마이저가 판단한 조인 방식
조인 방법에 대한 힌트를 지정하지 않을 경우 조인 방식은 옵티마이저의 판단에 따른다.
COUNT 함수 연산을 수행
SELECT 절의 연산을 수행
2. 인덱스 생성 (튜닝)
TB_CUST 테이블에 CUST_NM 컬럼으로 구성된 인덱스를 생성
통계 정보 생성
튜닝 후 SQL문
중첩 루프 세미 조인(Nested Loop Semi Join)
조건 만족 시 해당 조인 대상 행은 더 이상 스캔을 수행하지 않고 다음 행으로 넘어가는 조인 방식
Execute Plan
TB_CUST 테이블의 TB_CUST_IDX01 인덱스를 인덱스 범위 스캔(INDEX RANGE SCAN)합니다.
'인덱스 범위 스캔'이란 수직적 탐색을 통해 인덱스 리프 블록의 특정 위치로 이동한 후 지정된 범위를 스캔하는 방식
인덱스의 스캔 방식 중 가장 일반적인 방식
TB_CUST 테이블의 TB_CUST_IDX01 인덱스는 CUST_NM으로 이루어진 인덱스
해당 인덱스 스캔 후 CUST_ID를 가져오기 위해 테이블 랜덤 액세스(TABLE ACCESS BY INDEX ROWID)합니다.
TB_CUST 테이블의 기본키가 CUST_ID긴 하지만, 여기서는 CUST_NM 컬럼으로 이루어진 인덱스를 스캔하였기 때문에 테이블 랜덤 액세스는 불가피하다.
TB_CUST_IDX01 인덱스의 구성을 'CUST_NM + CUST_ID' 컬럼으로 한다면 해당 테이블 랜덤 액세스 부하를 줄일 수 있다.
TB_CUST 테이블의 CUST_ID 컬럼을 바탕으로 TB_ORD 테이블의 TB_ORD_IDX01 인덱스를 인덱스 범위 스캔(INDEX RANGE SCAN)을 하였다.
TB_ORD_IDX01 인덱스는 'CUST_ID + PRDT_CD'로 구성되어서 별도로 테이블 랜덤 액세스하지 않았다.
TB_CUST 테이블을 기준으로 TB_ORD 테이블과 중첩 세비 조인(NESTED LOOPS SEMI)를 한다.
TB_CUST 테이블을 기준으로 TB_ORD 테이블과의 조인 조건이 성립되면 해당 조인 대상 행에 대해 더는 스캔하지 않고 멈추게 된다.
COUNT 함수 연산을 수행한다.
SELECT절의 연산을 수행한다.
추가 튜닝
인덱스 컬럼을 추가하여 테이블 랜덤 엑세스를 없애고 인덱스 스캔만으로 데이터를 조회하는 기법
인덱스를 재생성하고 통계 정보를 생성
TB_CUST_IDX01의 인덱스를 CUST_NM, CUST_ID로 재생성
별도의 테이블 랜덤 액세스를 하지 않고 TB_CUST_IDX01 인덱스 만을 스캔하게 된다.
Execute Plan
TB_CUST 테이블의 TB_CUST_IDX01 인덱스를 인덱스 범위 스캔(INDEX RANGE SCAN)을 한다.
TB_ORD 테이블의 TB_ORD_IDX01 인덱스를 인덱스 범위 스캔(INDEX RANGE SCAN)을 한다.
TB_CUST_IDX01 인덱스를 기준으로 TB_ORD_IDX01 인덱스와 중첩 루프 세미 조인(NESTED LOOPS SEMI) 한다.
COUNT 함수 연산을 수행한다.
SELECT절의 연산을 수행한다.
인덱스 구성 컬럼을 추가하여 테이블 랜덤 액세스 제거하기
실습을 위한 테이블 생성
데이터 입력
기본키 생성
인덱스 생성
통계 정보 생성
쿼리 비교를 통한 분석
SQL 분석
ORD_DT 컬럼을 조건으로 주어 2015년의 주문 건을 검색
ORD_DT + ORD_NM + ORD_AMT로 구성된 TB_ORD_IDX01 인덱스를 이용
위 쿼리의 문제점
TB_ORD_IDX01 인덱스를 사용한 효율적인 인덱스 스캔을 하고 있다.
하지만 SELECT 절에 인덱스 컬럼이 아닌 다른 컬럼도 조회하고 있으므로 인덱스 스캔에 이은 테이블 랜덤 액세스 부하가 발생한다.
넓은 범위를 주로 조회하고 결과 건수가 많아진다면 DBMS에 큰 부하를 주게 된다.
Execute Plan
TB_ORD 테이블의 TB_ORD_IDX01 인덱스를 인덱스 범위 스캔(INDEX RANGE SCAN)한다.
TB_ORD_IDX01 인덱스의 리프 블록에 저장된 ROWID를 이용하여 테이블 랜덤 액세스(TABLE ACCESS BY INDEX ROWID)를 수행
인덱스 스캔에서 조건에 만족하는 ROWID가 많다면 자연스레 테이블 랜덤 액세스 부하가 발생한다.
ORDER BY와 GROUP BY 연산을 수행한다.
SELECT 절의 연산을 수행한다.
튜닝
인덱스 추가
튜닝 후 SQL 문
TB_ORD 테이블의 TB_ORD_IDX02 인덱스를 사용하도록 INDEX 힌트를 사용
해당 인덱스는 SALE_GB 컬럼과 PAY_GB 컬럼이 존재하기 때문에 테이블 랜덤 액세스를 생략할 수 있다.
테이블 랜덤 액세스가 사라졌다는 것은 인덱스 구성 컬럼만으로 SQL문의 요청 내용을 검색한 것을 뜻한다.
Execute Plan
TB_ORD 테이블의 TB_ORD_IDX02 인덱스를 인덱스 범위 스캔(INDEX RANGE SCAN)을 한다.
ORDER BY와 GROUP BY 연간을 수행한다.
SELECT 절의 연산을 수행한다.
Last updated