인덱스 기본

2023. 8. 22. 22:44·DB/SQL 튜닝
반응형

인덱스 구조 및 탐색

RDBMS 테이블에서 데이터를 찾는 방법은 두 가지다.

  • 테이블 전체 스캔
  • 인덱스 이용

인덱스는 큰 테이블에서 소량 데이터를 검색할 때 사용한다.

 

세부적인 인덱스 튜닝 방법의 핵심은 크게 두 가지다.

 

첫 번째는 인덱스 스캔 과정에서 발생하는 비효율을 줄이는 것이다.

즉, 인덱스 스캔 효율화 튜닝이다.

 

두 번째는 테이블 액세스 횟수를 줄이는 것이다.

인덱스 스캔 후 테이블 레코드를 액세스할 때 랜덤 I/O 방식을 사용하므로 이를 '랜덤 액세스 최소화 튜닝'이라고 한다.

 

둘 중 더 중요한 것은 랜덤 액세스 최소화 튜닝이다.

결국 SQL 튜닝은 랜덤 I/O와의 전쟁이다.

 

DB 성능이 느린 이유는 디스크 I/O 때문이다. 읽어야 할 데이터량이 많고, 그 과정에 디스크 I/O가 많이 발생할 때 느리다.

인덱스를 많이 사용하는 온라인 트랜잭션 처리 시스템은 디스크 I/O 중에서도 랜덤 I/O가 특히 중요하다.

인덱스 구조

  • 인덱스는 대용량 테이블에서 필요한 데이터만 빠르게 효율적으로 액세스 하기 위해 사용하는 객체다.
  • B에서 인덱스 없이 데이터를 검색하면 Full Scan을 한다.
  • 반면 인덱스를 이용하면 읽고 멈출 수 있는데, Range Scan이 가능하기 때문이다.
  • 범위 스캔이 가능한 이유는 인덱스가 정렬돼 있기 때문이다.
  • DBMS는 보통  B*Tree 인덱스를 사용한다.
  • Root, Branch, Leaf 노드가 존재한다.
  • 루트와 브랜치 블록의 각 레코드는 하위 블록에 대한 주소값을 갖는다.
  • 루트와 브랜치 블록은 LMC(Leftmost Child)라는 가장 왼쪽의 첫 번째 레코드가 있다.
  • LMC가 가리키는 주소로 찾아가면 블록에는 키 값을 가진 첫 번째 레코드보다 작거나 같은 레코드가 저장돼 있다.

B*Tree 예시

인덱스 탐색 과정은 수직적 탐색과 수평적 탐색으로 나눌 수 있다.

인덱스 수직적 탐색

  • 정렬된 인덱스 레코드 조건을 만족하는 첫 번째 레코드를 찾는 과정이다.
  • 인덱스 스캔 시작지점을 찾는 과정.

인덱스 수평적 탐색

  • 수직적 탐색을 통해 스캔 시작점을 찾았으면, 찾고자 하는 데이터가 더 안나타날 때까지 인덱스 리프 블록을 수평적으로 스캔한다.
  • 인덱스에서 본격적으로 데이터를 찾는 과정이다.
  • 인덱스 리프 블록끼리는 서로 앞뒤 블록에 대한 주솟값을 가지는, 양방향 연결리스트라서 수평적 탐색이 가능하다.

결합 인덱스 구조와 탐색

두 개 이상 컬럼을 결합해서 인덱스를 만들 수 있다.

결합 인덱스

인덱스 기본 사용법

인덱스를 사용한다는 것

  • 인덱스 컬럼(정확히, 선두 컬럼)을 가공하지 않아야 인덱스를 정상적으로 사용할 수 있다.
  • 정상적인 사용은 리프 블록에서 스캔 시작점을 찾아 거기서부터 스캔하다가 중간에 멈추는 동작을 의미한다.
  • 즉 리프 블록 일부만 스캔하는 Index Range Scan을 의미한다.
  • 인덱스 컬럼을 가공해도 인덱스를 사용할 수는 있지만, 스캔 시작점을 찾을 수 없고 멈출 수도 없어 리프 블록 전체를 스캔해야만 한다.

인덱스를 Range Scan 할 수 없는 이유

  • 인덱스 컬럼을 가공했을 때 인덱스를 정상적으로 사용할 수 없는 이유는 인덱스 스캔 시작점을 찾을 수 없기 때문이다.
  • 예시로는 like, or, null 값을 다른 값으로 치환해서 조건으로 사용, 날짜 가공 등의 예시가 있다.

더 중요한 인덱스 사용 조건

  • 인덱스를 Range Scan 하기 위한 가장 첫 번째 조건은 인덱스 선두 컬럼이 조건절에 있어야 한다는 사실이다. 가공하지 않은 상태로 말이다.
  • 인덱스를 탄다. == 인덱스를 Range Scan한다. 
  • 인덱스를 탄다고 꼭 튜닝이 잘 된 것은 아니다. 
  • 중요한 것은 인덱스 리프 블록에서 스캔하는 데이터의 양이다.

인덱스를 이용한 소트 연산 생략

  • 테이블과 달리 인덱스는 정렬 돼 있다. 이것이 바로 우리가 인덱스를 사용하는 이유다.
  • 인덱스가 정렬돼 있기 때문에 Range Scan이 가능하고, 소트 연산 생략 효과도 부수적으로 얻게 된다.
  • 만약 정렬 연산을 생략할 수 있게 인덱스가 구성돼 있지 않다면, SORT ORDER BY 연산 단계가 추가된다.

ORDER BY 절에서 컬럼 가공

  • 조건절이 아닌 ORDER BY 또는 SELECT-LIST에서 컬럼을 가공함으로 인해 인덱스를 정상적으로 사용할 수 없는 경우도 종종 있다.

SELECT-LIST에서 컬럼 가공

  • 인덱스를 이용해 정렬 연산 없이 최소 또는 최댓값을 빠르게 찾을 수 있다.

자동 형변환

  • 선두 컬럼을 조건절에서 가공하지 않았는데도 옵티마이저는 테이블 전체 스캔을 선택할 수 있다.
  • 예를 들어 생년월일 컬럼이 문자형인데 조건절에서 비교값을 숫자형으로 넣는 경우에 그렇다.
  • 종종 이런 자동 현변환에서 성능과 애플리케이션 품질에 문제가 생긴다.
  • 그러므로 자동 형변환에 의존해서는 절대 안된다.

인덱스 확장기능 사용법

Index Range Scan

  • 인덱스의 가장 일반적이고 정상적인 형태의 액세스 방식
  • 인덱스 루트에서 리프 블록까지 수직적으로 탐색한 후에 필요한 범위만큼만 스캔한다.
  • 인덱스를 Range Scan 하려면 선두 컬럼을 가공하지 않은 상태로 조건절에 사용해야 한다.

Index Full Scan

  • 수직적 탐색없이 인덱스 리프 블록을 처음부터 끝까지 수평적으로 탐색하는 방식이다.
  • Index Full Scan은 대개 데이터 검색을 위한 최적의 인덱스가 없을 때 차선으로 선택된다.

효용성

  • 인덱스 선두 컬럼이 조건절에 없으면 옵티마이저는 먼저 Table Full Scan을 고려한다.
  • 대용량 테이블이어서 Table Full Scan에 따른 부담이 크다면, 옵티마이저는 인덱스 활용을 다시 고려하지 않을 수 없다.
  • 인덱스 스캔 단계에서 대부분 레코드를 필터링하고 아주 일부만 테이블을 액세스하는 상황이라면,
  • 면적이 큰 테이블보다 인덱스를 스캔하는 쪽이 유리하다.
  • 그럴 때 옵티마이저는 Full Scan 방식을 선택한다.

인덱스를 이용한 소트 연산 생략

  • 인덱스를 Full Scan하면 Range Scan과 마찬가지로 결과집합이 인덱스 컬럼 순으로 정렬된다.
  • 따라서 Sort Order By 연산을 생략할 목적으로 사용할 수도 있다.

Index Unique Scan

  • 수직적 탐색만으로 데이터를 찾는 스캔 방식으로서, Unique 인덱스를 '=' 조건으로 탐색하는 경우에 작동한다.
  • Unique 인덱스가 존재하는 컬럼은 중복 값이 입력되지 않게 DBMS가 데이터 정합성을 관리해준다.
  • 따라서 해당 인덱스 키 컬럼을 모두 '=' 조건으로 검색할 때는 데이터를 한 건 찾는 순간 더 이상 탐색할 필요가 없다.

Index Skip Scan

  • 이 스캔 방식은 조건절에 빠진 인덱스 선두 컬럼의 Distinct Value 개수가 적고 후행 컬럼의 Distinct Value 개수가 많을 때 유용하다.
  • 예를 들어 고객 테이블에서 Distinct Value 개수가 가장 적은 컬럼은 성별이고, Distinct Value 개수가 가장 많은 컬럼은 '고객번호'다.
  • 루트 또는 브랜치 블록에서 읽은 컬럼 값 정보를 이용해 조건절에 부합하는 레코드를 포함할 '가능성이 있는' 리프 블록만 골라서 액세스하는 스캔 방식이다.

Index Skip Scan이 작동하기 위한 조건

  • Index Skip Scan은 Distinct Value 개수가 적은 선두 컬럼이 조건절에 없고 후행 컬럼의 Distinct Value 개수가 많을 때 효과적이다.
  • 인덱스 선두 컬럼이 없을 때만 Index Skip Scan이 작동하는 것은 아니다.
  • 선두 컬럼에 대한 조건절은 있고, 중간 컬럼에 대한 조건절이 없는 경우에도 사용할 수 있다.
  • Distinct Value가 적은 두 개의 선두 컬럼이 모두 조건절에 없는 경우에도 사용할 수 있다.

Index Range Scan이 불가능하거나 효율적이지 못한 상황에서 Index Skip Scan이 종종 빛을 발한다.

부분범위 처리가 가능하다면 Index Full Scan이 도움이 되기도 한다.

Index Fast Full Scan

  • Index Fast Full Scan은 Index Full Scan보다 빠르다.
  • 빠른 이유는, 논리적인 인덱스 트리 구조를 무시하고 인덱스 세그먼트 전체를 Multiblock I/O 방식으로 스캔하기 때문이다.
  • Multiblock I/O 방식을 사용하므로 디스크로부터 대량의 인덱스 블록을 읽어야할 때 큰 효과를 발휘한다.
  • 결과 집합 순서를 보장하지 않으며, 병렬 스캔이 가능하다.
  • 인덱스에 포함된 컬럼으로만 조회할 때 사용 가능하다.

Index Range Scan Descending

  • Index Range Scan과 기본적으로 동일한 스캔 방식
  • 인덱스를 뒤에서부터 앞쪽으로 스캔하기 때문에 내림차순으로 정렬된 결과집합을 얻는다는 점만 다르다.

 

참고자료

친절한 SQL 튜닝 2장

반응형
'DB/SQL 튜닝' 카테고리의 다른 글
  • NL 조인 쿼리 튜닝
  • 조인 튜닝 (1)
  • 인덱스 튜닝
  • SQL 처리 과정과 I/O
Debin
Debin
공부 기록을 남기며 게시글 리팩토링을 진행하는 블로그입니다.
  • Debin
    리팩토링하는 블로그
    Debin
  • 전체
    오늘
    어제
    • 분류 전체보기
      • DB
        • DB 기초
        • MySQL
        • SQL 튜닝
      • OS
      • Network
      • Git
      • 디지털콘텐츠기획
      • 소프트웨어공학
      • 코딩테스트
        • 프로그래머스
        • 백준
        • 인프런
      • 공부 일지
      • 독서
        • 클린코드
        • 일상 속 사물이 알려주는 웹 API 디자인
        • 토비의 스프링
        • 객체지향의 사실과 오해
        • 자바 잘 읽는 법
      • 기록 및 회고
      • Cloud
        • AWS
      • 개발
        • Java
        • Spring Core
        • Spring MVC
        • Spring DB
        • Spring Boot
        • Spring Security
        • Spring Batch
        • JPA
        • Test
        • Android
      • 대외활동
        • UMC SERVER
        • 카엔프 SW 아카데미
      • 프로젝트
      • Docker
      • Gradle
      • ELK
      • 실무 이야기
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • 깃허브
  • 공지사항

    • 본인 깃허브입니다!
  • 인기 글

  • 태그

    트랜잭션
    Java
    코딩 #개발자 #노마드북클럽 #노개북
    spring mvc
    인덱스
    자바
    리눅스
    운영체제
    AWS
    JPA
    container
    docker
    프록시
    스프링 부트
    SQL
    토비의 스프링
    AOP
    spring
    도커
    redis
    test
    데이터베이스
    객체
    innodb
    ORM
    객체지향
    컨테이너
    spring boot
    mysql
    스프링
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
Debin
인덱스 기본
상단으로

티스토리툴바