인덱스의 종류와 각각의 특징은 무엇인가요?

김상욱·2024년 12월 14일

인덱스의 종류와 각각의 특징은 무엇인가요?

B-Tree Index

  • B-Tree(Balanced Tree) 구조를 기반으로 하며, 균형 잡힌 다진 트리로 구성. 모든 리프 노드가 동일한 깊이를 가지므로 검색, 삽입, 삭제 시 성능이 균일

  • 범위 검색에 최적화되어 있어 특정 범위 내의 데이터를 빠르게 조회할 수 있음

  • 인덱스 키가 정렬되어 저장되므로 정렬된 순서로 데이터를 효율적으로 검색

  • 대부분의 관계형 데이터베이스 시스템(MySQL, PostgreSQL, Oracle, SQL Server 등)에서 기본 인덱스로 사용

  • 단일 열 인덱스 및 복합 열 인덱스(여러 열을 결합한 인덱스)로 생성 가능

  • 일반적인 검색, 정렬, 조인 연산에 적합

  • 높은 검색 성능을 지녔으며 범위 검색에 유리하고 데이터 삽입, 삭제, 업데이트가 비교적 효율적이다.
    but
    대량의 데이터 삽입 시 인덱스 유지 관리 오버헤드 발생. 공간을 많이 차지할 수 있음.

Hash Index

  • 해시 함수를 사용하여 키를 해시 테이블의 특정 버킷에 매핑

  • 정확히 일치하는 값의 검색에 매우 빠름

  • 해시 함수의 특성상 데이터가 정렬되지 않음

  • 주로 메모리 기반 데이터베이스나 NoSQL 데이터베이스에서 사용

  • MySQL의 Memory 스토리지 엔진이나 PostgreSQL의 일부 확장 기능에서 사용

  • 동등 검색에 대해 매우 빠른 성능 제공. 단순한 구조로 인한 낮은 검색 오버헤드
    but

  • 범위 검색 불가능. 해시 충돌이 발생할 경우 성능 저하. 데이터 정렬이나 순서 기반 검색에 부적합

Bitmap Index

  • 각 키 값에 대해 비트맵(Bit Map)을 사용하여 해당 키 값이 있는 레코드를 표시

  • 낮은 카디널리티(고유 값의 수가 적은 경우)에 특히 효율적

  • 데이터 웨어하우스나 OLAP(Oline Analytical Processing) 환경에서 주로 사용

  • 성별, 상태, 등과 같이 고유 값의 수가 적은 열에 적합

  • 복잡한 논리 연산(AND, OR, NOT 등)을 빠르게 수행. 비트맵을 사용하여 공간을 절약
    but

  • 높은 카디널리티(고유 값의 수가 많은 경우)에서는 비효율적. 데이터 변경(삽입, 삭제, 업데이트) 시 비트맵 재구성이 필요하여 유지 관리가 복잡

Clustered Index

  • 테이블의 데이터 자체가 인덱스의 순서에 따라 물리적으로 정렬됨

  • 하나의 테이블의 하나의 클러스터드 인덱스만 존재할 수 있음.

  • 주로 기본 키(primary key)에 설정되며, 자주 검색되는 범위 쿼리에 유리

  • 데이터와 인덱스가 동일한 구조이므로 데이터 접근이 빠름. 범위 검색, 정렬된 순서의 데이터 조회에 최적화
    but

  • 데이터 삽입, 삭제, 업데이트 시 물리적 재배치가 필요하여 성능 저하

  • 클러스터드 인덱스가 있는 경우, 비클러스터드 인덱스가 클러스터드 인덱스의 키를 참조해야 하므로 추가적인 저장 공간이 필요.

Non-Clustered Index

  • 데이터와 별도로 인덱스 구조가 존재하며, 인덱스는 데이터의 위치를 가리키는 포인터를 포함.

  • 하나의 테이블에 여러 개의 넌클러스터드 인덱스를 생성할 수 있음

  • 다양한 검색 조건에 대응하기 위해 사용

  • 자주 사용되는 비클러스터드 인덱스를 생성하여 검색 성능 향상

  • 여러 개의 넌클러스터드 인덱스를 통해 다양한 쿼리에 대응 가능. 데이터의 물리적 정렬에 영향을 주지 않음
    but

  • 인덱스가 많아질수록 쓰기 작업(삽입, 업데이트, 삭제) 시 오버헤드 증가. 추가적인 저장 공간이 필요.

FUll-Text Index

  • 텍스트 데이터를 효율적으로 검색하기 위해 설계된 인덱스.

  • 단어 기반의 검색, 자연어 검색, 부분 일치 검색 등을 지원

  • 대량의 텍스트 데이터(예: 문서, 기사, 블로그 등)에서 키워드 검색을 빠르게 수행

  • MySQL의 FULLTEXT 인덱스, PostgreSQL의 Full Text Search, SQL Server의 Full-Text Index 등이 있음.

  • 복잡한 텍스트 검색 기능 제공. 대량의 텍스트 데이터에서 효율적인 검색 가능
    but

  • 인덱스 생성 및 유지 관리에 높은 비용이 소요. 정확한 일치 검색에는 적합하비 않음.

Unique Index

  • 인덱스가 설정된 열의 값이 중복되지 않도록 강제

  • 클러스터드 또는 넌클러스터드 인덱스로 생성 가능

  • 데이터의 무결성을 유지하기 위해 사용.

  • 기본 키나 고유 제약이 필요한 열에 주로 설정

  • 중복 데이터를 방지하고 데이터 무결성 보장
    but

  • 인덱스 유지에 따른 추가적인 오버헤드, 중복된 값을 삽입하려 할 경우 오류 발생

Composite Index

  • 두 개 이상의 열을 결합하여 생성된 인덱스

  • 인덱스의 열 순서가 쿼리 성능에 영향을 미침

  • 여러 열을 동시에 검색하거나 정렬하는 쿼리에 최적화

  • 다중 조건 검색 시 인덱스 활용도를 높임

  • 복합 조건 검색에서 높은 성능. 여러 열을 하나의 인덱스로 관리하여 저장 공간 절약 가능.
    but

  • 인덱스 생성 및 유지 관리 비용 증가. 열 순서에 따라 인덱스의 효율성이 달라짐.

Partial Index

  • 테이블의 일부분에 대해서만 인덱스를 생성

  • 특정 조건을 만족하는 행에 대해서만 인덱스를 생성

  • 테이블의 전체 데이터가 아닌 특정 조건에 맞는 데이터에 대한 검색을 최적화

  • 예를 들어, 활성 상태인 레코드에만 인덱스를 생성하여 검색 성능 향상

  • 인덱스 크기 감소로 저장 공간 절약. 인덱스 유지 관리 오버헤드 감소. 특정 조건의 쿼리 성능 향상.
    but

  • 조건에 맞지 않는 쿼리에는 인덱스 활용 불가. 인덱스 설계가 복잡해질 수 있음.


Java Spring 백엔드 개발자 입장에서 위 인덱스 종류를 실습하며 학습할 수 있는 방법을 제안하겠습니다. 아래 단계는 데이터베이스 설계와 쿼리 작성, 그리고 성능 최적화를 직접 경험할 수 있도록 구성되어 있습니다.


1. 실습 환경 준비

  • 필수 도구: MySQL 또는 PostgreSQL (DBMS), IntelliJ IDEA/Eclipse (IDE), Spring Boot (Java 기반 백엔드 개발), DBeaver (DB 관리 GUI 툴).
  • Spring Boot 프로젝트 설정:
    • Spring Data JPA, Spring Web, MySQL Driver 의존성 추가.
    • MySQL 또는 PostgreSQL 데이터베이스 연결 설정.
    • 간단한 엔티티(Entity)와 리포지토리(Repository) 작성.

2. 실습 주제별 가이드

1) B-Tree Index

실습 내용:

  • id, name, created_date를 가진 간단한 테이블 생성.
  • 테이블에 기본 키(Primary Key)와 단일 열 인덱스 생성.
  • 범위 검색 쿼리를 실행하고 인덱스 사용 여부 확인.

실습 코드:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_date DATE,
    INDEX idx_created_date (created_date)
);

-- 범위 검색 쿼리
EXPLAIN SELECT * FROM users WHERE created_date BETWEEN '2023-01-01' AND '2023-12-31';

Spring 실습:

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    List<User> findByCreatedDateBetween(LocalDate startDate, LocalDate endDate);
}

결과 확인:

  • EXPLAIN 결과에서 idx_created_date 인덱스가 사용되는지 확인.
  • 데이터 삽입 후 범위 검색 성능을 비교.

2) Hash Index

실습 내용:

  • MySQL Memory 엔진에서 해시 인덱스를 생성하고, 동등 검색 쿼리의 성능을 확인.

실습 코드:

CREATE TABLE users_memory (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(50),
    INDEX idx_email (email) USING HASH
) ENGINE=MEMORY;

-- 동등 검색 쿼리
EXPLAIN SELECT * FROM users_memory WHERE email = 'test@example.com';

Spring 실습:

  • 메모리 엔진 사용은 Spring에서 잘 쓰이지 않으므로 SQL로만 실습.

결과 확인:

  • 동등 검색 쿼리 성능이 B-Tree보다 빠른지 확인.
  • 범위 검색 불가를 확인.

3) Composite Index

실습 내용:

  • 두 개의 컬럼(name, created_date)을 결합한 복합 인덱스를 생성.
  • 컬럼 순서에 따라 쿼리 성능이 달라지는 사례를 확인.

실습 코드:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    created_date DATE,
    INDEX idx_name_date (name, created_date)
);

-- 쿼리 1: name이 조건에 포함됨 -> 인덱스 사용
EXPLAIN SELECT * FROM users WHERE name = 'John' AND created_date > '2023-01-01';

-- 쿼리 2: created_date만 조건에 포함됨 -> 인덱스 미사용
EXPLAIN SELECT * FROM users WHERE created_date > '2023-01-01';

Spring 실습:

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    List<User> findByNameAndCreatedDateAfter(String name, LocalDate createdDate);
    List<User> findByCreatedDateAfter(LocalDate createdDate);
}

결과 확인:

  • 복합 인덱스 순서에 따른 성능 차이 확인.
  • idx_name_date 인덱스가 언제 활용되는지 분석.

4) Clustered Index vs Non-Clustered Index

실습 내용:

  • MySQL에서는 클러스터드 인덱스가 Primary Key로 자동 생성됨.
  • PostgreSQL에서 클러스터드 인덱스와 넌클러스터드 인덱스 성능 차이 확인.

실습 코드:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY, -- Clustered Index
    name VARCHAR(50),
    created_date DATE,
    INDEX idx_name (name) -- Non-Clustered Index
);

-- 클러스터드 인덱스 활용
EXPLAIN SELECT * FROM users WHERE id = 1;

-- 넌클러스터드 인덱스 활용
EXPLAIN SELECT * FROM users WHERE name = 'John';

결과 확인:

  • 클러스터드 인덱스와 넌클러스터드 인덱스가 어떤 쿼리에서 사용되는지 비교.

5) Full-Text Index

실습 내용:

  • 블로그 게시글 데이터를 저장하고 텍스트 검색 인덱스를 추가.
  • MATCH()AGAINST()를 사용한 텍스트 검색 성능 확인.

실습 코드:

CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255),
    content TEXT,
    FULLTEXT INDEX idx_fulltext_content (content)
);

-- Full-Text 검색
EXPLAIN SELECT * FROM posts WHERE MATCH(content) AGAINST('Spring Framework');

Spring 실습:

  • Spring JPA의 Native Query를 활용:
@Query(value = "SELECT * FROM posts WHERE MATCH(content) AGAINST(?1)", nativeQuery = true)
List<Post> searchByContent(String keyword);

6) Partial Index

실습 내용:

  • 조건부 인덱스를 생성하여 활성 상태 데이터만 인덱싱.
  • 쿼리 최적화 확인.

실습 코드:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    is_active BOOLEAN,
    INDEX idx_active_users (id) WHERE is_active = TRUE
);

-- Partial Index 활용 쿼리
EXPLAIN SELECT * FROM users WHERE is_active = TRUE;

결과 확인:

  • 활성 상태 데이터만 대상으로 하는 쿼리가 성능 최적화되는지 확인.

3. 결과 분석 및 학습 목표

  1. 쿼리 최적화: 인덱스가 쿼리에 미치는 영향을 EXPLAIN으로 확인.
  2. Spring 연동: JPA 리포지토리에서 쿼리가 어떻게 동작하고, 인덱스를 얼마나 활용하는지 분석.
  3. 적용 시나리오 파악:
    • 복합 인덱스와 단일 인덱스의 적합성.
    • Full-Text와 범위 검색의 차이.
    • 동등 검색 시 해시 인덱스와 B-Tree 인덱스의 장단점.

결론

이러한 실습을 통해 인덱스 설계, 활용, 성능 분석에 대한 실질적인 이해를 얻을 수 있습니다. 또한, Spring Boot와 데이터베이스의 연계 작업에서 인덱스를 활용한 효율적인 쿼리 작성 능력을 기를 수 있습니다.

추가로 특정 실습 부분에서 더 깊이 다루길 원하면 알려주세요! 😊

0개의 댓글