권순용의 DB 이야기
진화된 인덱스 스캔을 이용하자 1 4 99,999+

by axiom 인덱스 스킵스캔 SKIP SCAN INDEX_SS INDEX_SS_DESC SQL 최적화 [2013.04.25]


인덱스 스캔은 데이터를 액세스하기 위해서는 매우 중요한 요소이다. 테이블의 데이터 중 적은 데이터를 액세스하는 경우 데이터 액세스를 빠르게 수행하기 위해서는 인덱스를 이용해야 하는 것은 당연한 이야기이다. 이와 같다면 해당 테이블에는 몇 개의 인덱스가 필요한 것인가?

시스템에서 매우 많은 액세스가 존재하는 테이블의 경우에는 많은 개수의 인덱스가 필요할 것이다. 인덱스가 많이 필요하면 해당 테이블에 발생하는 DML 작업은 자연히 성능 저하를 발생시키게 된다.

모든 액세스를 최적화하는 것은 매우 중요하지만 그렇게 수행하기 위해 인덱스를 모두 생성하는 것은 무척 어렵다. 그렇다면 어떻게 우리는 인덱스를 생성해 최적을 유지해야 하는가? 이것이 바로 진화된 인덱스 스캔을 이용해야 하는 이유일 것이다.

인덱스는 어떠한가? 인덱스를 구성하는 첫 번째 컬럼이 WHERE 조건에 존재하지 않는다면 해당 인덱스를 이용할 수 없게 된다. 이는 인덱스에 관심이 있는 사람이라면 모두 아는 사실일 것이다.

이와 같은 이유에 의해 많은 액세스가 존재하는 테이블에는 많은 인덱스가 필요하게 된다. 이를 해결할 수 있는 방법 중에 하나가 인덱스 스킵 스캔이다. 그렇기 때문에 인덱스 스킵 스캔에 대해 정확히 이해하는 것은 인덱스를 효과적으로 적용하기 위해 매우 중요한 일이다.

인덱스 스킵 스캔의 방식을 이해하자

인덱스 스킵 스캔은 인덱스의 첫 번째 컬럼이 WHERE 조건에 존재하지 않아도 인덱스를 이용할 수 있는 인덱스 액세스 방식이다. 아래의 예제를 확인해 보자.

SELECT 사원명, 위치, 이메일
  FROM 사원
 WHERE 위치 = '서울'

위의 SQL에서 사원 테이블에는 부서번호+위치 인덱스만이 존재한다고 가정하자. 이와 같다면 해당 SQL은 어떠한가? 일반적으로 위의 SQL은 인덱스를 이용할 수 없게 된다.

이는 인덱스의 첫 번째 컬럼이 WHERE 조건에 존재하지 않기 때문이다. 그렇다면 위의 SQL은 위치 인덱스를 생성하지 않는다면 인덱스를 이용할 수 없는 것인가? 진화된 인덱스 스캔 방식인 인덱스 스킵스캔을 이용한다면 인덱스를 이용할 수 있게 된다.

인덱스 스킵스캔을 이용하는 방법에 대해 언급하기 전에 과거에는 어떻게 해당 SQL을 최적화했는지를 확인해 보자. 위의 SQL은 아래와 같이 변경해 최적화를 수행한다.

SELECT 사원명, 위치, 이메일
  FROM 사원
 WHERE 위치 = '서울'
   AND 부서번호 IN ('10',……,'200');

위의 SQL은 어떠한가? 만약, WHERE 조건에 부서번호 조건을 추가하고 해당 조건에 모든 부서번호를 하나씩 설정한다면 위의 SQL은 앞서 언급한 SQL과 동일한 결과가 추출될 것이다.

그렇다면 위의 SQL은 부서번호+위치 인덱스를 이용할 수 있겠는가? 위의 SQL은 당연히 부서번호+위치 인덱스를 이용할 수 있게 된다. 이와 같이 SQL에 더미 조건을 추가해 SQL을 최적화하는 경우가 많다.

여기서 문제점은 무엇인가? 부서번호 조건의 개수가 많다면 IN 절에 하나하나 모두 적어주는 것이 쉽지 않다는 것이다. 그래서 아래와 같이 SQL을 최적화해 부서번호+위치 인덱스를 이용하는 경우도 있다.

SELECT 사원명, 위치, 이메일
  FROM 사원
 WHERE 위치 = '서울'
   AND 부서번호 IN 
     ( SELECT 부서번호 
         FROM 부서
     );

위의 SQL과 같이 부서번호 조건에 모든 부서번호 값을 제공할 수 있게 설정한다. 이와 같이 SQL을 수행한다면 모든 부서번호 값을 부서 테이블로부터 제공받을 수 있기 때문에 해당 SQL은 부서번호+위치 인덱스를 이용할 수 있게 된다.

물론, 부서 테이블을 액세스하는 부분은 작은 테이블이기 때문에 부담이 많지는 않게 된다.

SQL에 조건이 존재하지 않아 인덱스를 이용하지 못하는 경우 수동으로 더미 조건을 추가해 인덱스를 이용하게 했다.

이와 같은 방법을 데이터베이스에서 자동으로 구현하기 위해 인덱스 스킵 스캔을 만들게 되었으며 이를 이용한다면 인덱스의 첫 번째 컬럼이 WHERE 조건에 존재하지 않아도 인덱스를 이용할 수 있게 된다. 이것이 바로 진화된 인덱스 스캔의 방식이다.

인덱스 스킵 스캔을 주의하자

인덱스의 첫 번째 컬럼이 WHERE 조건 절에 존재하지 않는 경우 위와 같이 인덱스 스킵 스캔을 이용한다면 모든 문제를 해결 할 수 있을까? 그것은 아닐 것이다. 아래의 예제를 확인해 보자.

SELECT 고객명, 사용액
 FROM 거래내역
WHERE 위치 = '서울'
  AND 카드번호 IN 
    ( SELECT 카드번호 
        FROM 카드_마스터
    );

위의 SQL에서 거래내역 테이블에는 카드번호+위치 인덱스가 존재한다고 가정하자. 위의 SQL은 카드번호+위치 인덱스를 이용하기 위해서 카드번호 조건을 WHERE 조건에 설정한 것이다.

위의 SQL은 어떠한가? 해당 카드_마스터 테이블에는 1,000만 개의 카드번호가 존재한다고 가정하자. 그렇다면 IN 절에서 제공해주는 카드번호의 개수 또한 1,000만 개가 될 것이다.

이와 같다면 IN 절에서 너무 많은 개수의 카드번호를 거래내역 테이블에 전달하기 때문에 성능 저하가 발생할 수 있게 된다. 결국, 더미 조건에서 많은 개수의 상수를 제공한다면 이는 성능 저하를 발생시키게 되므로 이와 같이 이용하면 안 될 것이다.

인덱스 스킵스캔도 이와 별반 다르지 않다. 그렇다면 인덱스 스킵 스캔은 어떻게 유도할 수 있겠는가?

인덱스 스킵스캔은 아래와 같은 방법을 이용해 유도할 수 있다.

SELECT /*+ INDEX_SS(A IDX_1) */ 
        고객명, 사용액
  FROM 거래내역
 WHERE 위치 = '서울'

이와 같이 SQL에 힌트를 이용해 인덱스 스킵 스캔을 유도할 수 있다. 힌트를 이용한다면 위의 SQL은 카드번호+위치 인덱스를 이용해 조건을 만족하는 데이터를 결과로 추출할 수 있게된다.

물론, 인덱스 스킵 스캔은 옵티마이저에 의해 선택될 수도 있으며 상황에 따라서는 SQL 최적화를 위해 수동으로 인덱스 스킵스캔을 유도할 수 있다.

인덱스 스킵 스캔은 INDEX_SS 힌트를 이용해 유도할 수 있으며 이는 INDEX_SS_ASC를 의미하게 된다. 내림차순으로 인덱스를 액세스하기 위해서는 INDEX_SS_DESC 힌트를 설정하면 된다.

인덱스를 액세스하는 방법은 다양하게 존재한다. 이중 인덱스 스킵 스캔은 진화된 형태의 인덱스 스캔이다. 인덱스의 첫 번째 컬럼이 WHERE 조건 절에 존재하지 않아도 인덱스를 이용할 수 있다는 것은 놀라운 사실이다.

이와 같은 인덱스 스킵 스캔을 정확히 이해하고 이용한다면 SQL을 최적화하는 데 많은 도움이 된다.

SQL의 최적화는 먼 곳에 있는 것이 아니다. 하나하나의 지식을 경험을 통해 습득해 실무에 적용하는 것이 SQL 최적화의 방법이다. 이 중 인덱스 스킵 스캔도 중요한 역할을 수행하게 된다.

다음 강의에는 SQL에서 발생하는 정렬에 대해 확인해보자.

- 강좌 URL : http://www.gurubee.net/lecture/2266

- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^

- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.

by 허승호 [2013.05.09 16:29:47]

좋은 정보네요~ 감사합니다.^^

by 참된신자 [2014.08.07 09:37:50]

감사합니다 :D


by 심심해죽을맛 [2016.03.29 14:07:46]

감사합니다 


by 광이 [2018.10.23 11:07:57]

감사합니다

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입