Post

01 index

01 index

1. Note

  • JPA를 사용하면서 인덱스가 과거와는 조금 패러다임을 가지게됨
    • 기존에는 쿼리 위주로 데이터를 조회하고 한번에 작업하는 형태
    • JPA는 빠르게 조회해서 ORM기준으로 무엇인가 처리하기 때문에
    • Query 자체는 굉장히 단순해짐.
  • Index는
    • 너무 무리하지말고 흐름만 파악하고
    • 기본에 충실하는 형태로 활용할 것

2. index

1. 인덱스

  • 데이터베이스에서 특정 컬럼의 값을 기준으로 데이터를 빠르게 조회하기 위해 별도로 유지되는 자료구조
  • 테이블의 일부 컬럼 값을 정렬된 구조(B-Tree 등)로 저장하고,
  • 해당 값에 대응하는 데이터의 위치를 함께 관리하여 검색 성능을 향상시키는 객체
  • Point
    • 실제 데이터와 별도로 저장됨
    • 조회 성능을 위해 존재
    • 대신 쓰기 성능과 저장공간을 희생
    • 내부적으로 정렬된 구조 유지

2. 인덱스의 필요성

  • 장점
    • 조회 성능 향상 (검색, 조건 조회, 정렬)
    • 디스크 I/O 감소로 전체 성능 개선
    • 대용량 데이터 처리에 유리
  • 단점
    • INSERT / UPDATE / DELETE 성능 저하
    • 추가 저장공간 필요
    • 잘못 설계 시 성능 오히려 악화

3. 인덱스 종류와 기준

  • Note
    • 대부분의 일반 인덱스는 B-Tree(B+Tree)로 구현되지만, 인덱스 종류 자체가 B-Tree를 의미하는 것
    • 모든 인덱스가 B-Tree로 구현된건 아님 X
  • 인덱스 종류 (논리적 분류)

    종류설명특징사용 상황
    단일 인덱스 (Single Index)하나의 컬럼에 대한 인덱스가장 기본WHERE email = ?
    복합 인덱스 (Composite Index)여러 컬럼을 묶은 인덱스컬럼 순서 중요WHERE name + age
    유니크 인덱스 (Unique Index)중복 값 허용 안함데이터 무결성 보장이메일, 아이디
    클러스터드 인덱스 (Clustered Index)실제 데이터가 인덱스 순으로 정렬테이블당 1개PK (기본키)
    논클러스터드 인덱스 (Non-Clustered)별도의 인덱스 구조데이터와 분리일반 검색
    커버링 인덱스 (Covering Index)쿼리에 필요한 컬럼을 모두 포함테이블 접근 없음성능 최적화
    풀텍스트 인덱스 (Full-Text)문자열 검색 최적화LIKE보다 빠름검색 기능
    해시 인덱스 (Hash Index)해시 기반= 검색만 빠름정확 일치
  • 인덱스 구조 (물리적 구현)
구조설명
B-Tree / B+TreeDB 기본 인덱스 구조
Hash= 검색 특화
(기타)Bitmap 등

3. B-Tree

1. 비트리(B-Tree)

  • 디스크 기반 DB에서 읽기/쓰기 균형이 가장 좋은 구조라서 많이 사용
  • 한번 연산할때마다 넓은 범위의 key들을 제거가 가능함.

2. B-Tree

  • 노드(Node) 구조
    1
    2
    3
    4
    5
    6
    
     # 하나의 노드에 여러 개의 key(값) 저장
     # key 사이마다 자식 노드 포인터 존재
      
     [ key1 | key2 | key3 ]
        ↓      ↓      ↓
      child  child  child  
    
  • 정렬 규칙
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    # 이거 아래의 자식들도 값으로 의미가 있고
    # 이거 자체도 의미가 있음
    # 10을 찾는 경우 아래 노드까지 안가고 여기서 멈출수도 있음
    # 계속 최소 Key값, 최대 key값 수를 맞춰가면서 계속 정렬함.
    [ 10 | 20 | 30 ]
    10보다 작은 값 → 왼쪽 자식
    10~20 사이 → 두 번째 자식
    20~30 사이 → 세 번째 자식
    30보다 큰 값 → 마지막 자식
    
  • 트리 구조 특징
  • 모든 리프 노드가 같은 깊이 (균형 유지)
  • 트리 높이가 낮음
  • 검색 시 위 → 아래로 내려감

3. 주로 사용하는 이유

  • 디스크 I/O 최소화
    • 한 노드에 많은 키를 저장 (fan-out 큼)
    • 트리 높이가 낮음
    • 적은 디스크 접근으로 데이터 조회 가능
  • 정렬된 구조 유지
    • 항상 값이 정렬된 상태
    • 범위 조회 (>, <, BETWEEN) + ORDER BY에 매우 유리
  • 읽기/쓰기 균형 잡힌 성능
    • 조회: O(log N)
    • 삽입/삭제: 균형 유지하며 안정적 수행
    • 특정 상황에서 성능이 급격히 나빠지지 않음
  • DB 저장 구조(페이지)와 궁합이 좋음
    • 노드 = 디스크 페이지 단위
    • 한 번 읽을 때 많은 데이터 확보
    • DB 내부 구조에 최적화된 형태

4. B-Tree 장단점

  • 장점
    • 빠른 검색 성능 (O(log N))
    • 정렬된 구조로 범위 조회에 강함 (>, <, BETWEEN)
    • 트리 균형 유지로 안정적인 성능 보장
    • 디스크 I/O 최소화에 유리 (높이가 낮음)
  • 단점
    • 구조가 복잡하여 구현 및 관리 비용 존재
    • 삽입/삭제 시 노드 분할/병합 발생 → 쓰기 비용 증가
    • 메모리 기반 구조(해시 등)보다 단일 조회는 느릴 수 있음
    • 완전 랜덤 접근보다 순차/범위 중심에 최적화됨

4. 인덱스를 거는 방법

1. Primary Key

  • 테이블의 각 행을 고유하게 식별하기 위해 사용되는 특별한 인덱스
  • 특징
    • 테이블당 하나의 PRIMARY KEY만 정의할 수 있음
    • PRIMARY KEY는 중복을 허용하지 않음
    • PRIMARY KEY는 NULL 값을 허용하지 않음
    • PRIMARY KEY 생성 시 대부분 인덱스가 자동으로 생성됨
    • 일부 DB에서는 PRIMARY KEY가 클러스터형 인덱스로 사용됨
  • 예시
    1
    2
    3
    4
    
     CREATE TABLE user (
        id INT PRIMARY KEY,            -- user_id가 기본 키로 지정됨
        email VARCHAR(255) NOT NULL    -- email은 NULL을 허용하지 않음
     );
    

2. Unique Index

  • 특정 컬럼(또는 컬럼들의 조합)에 대해 중복된 값을 허용하지 않는 인덱스
  • 특징
    • 한 테이블에 여러 개의 UNIQUE INDEX를 생성할 수 있음
    • UNIQUE INDEX는 NULL 값을 허용
    • NULL 허용 방식은 DBMS마다 다름
    • UNIQUE INDEX는 중복 값을 허용하지 않음
  • 예시
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    CREATE TABLE user (
      user_id INT PRIMARY KEY,
      email VARCHAR(255) NOT NULL,
      username VARCHAR(50)
    );
      
    -- email 컬럼에 UNIQUE 인덱스 생성 (중복 이메일 허용 안 함)
    CREATE UNIQUE INDEX idx_user_email ON users(email);
      
    -- username 컬럼에도 UNIQUE 인덱스 생성 (NULL 허용, 중복 사용자명 허용 안 함)
    CREATE UNIQUE INDEX idx_user_username ON users(username);
    

3. 복합 인덱스 (Composite Index)

  • 두 개 이상의 컬럼을 조합하여 생성된 인덱스
  • 특징
    • 여러 컬럼이 AND 조건으로 함께 사용될 때 성능을 향상
    • 복합 인덱스는 정의된 컬럼 순서에 따라 동작
    • 인덱스는 선행 컬럼부터 순서대로 사용
    • 선행 컬럼을 포함하는 조건에서만 인덱스를 활용할 수 있음
  • 예시
    1
    2
    3
    4
    5
    
    -- user 테이블에 user_id와 created_at 컬럼으로 복합 인덱스 생성
    CREATE INDEX idx_user_user_id_created_at ON user (user_id, status);
      
    -- 특정 사용자의 특정 날짜 이후 주문 조회
    SELECT * FROM user WHERE user_id = 1 AND created_at > '2025-01-16';
    

5. 인덱스 설계 시 주의사항

1. 과도한 인덱스 생성 지양

  • 너무 많은 인덱스는 데이터 삽입(INSERT), 삭제(DELETE), 갱신(UPDATE) 작업의 성능을 심각하게 저하시킴
  • 데이터가 변경될 때마다 관련 인덱스도 함께 갱신해야 하므로 추가적인 오버헤드가 발생함
  • 권장 사항
    • 실제로 자주 사용되는 쿼리에만 인덱스를 생성해야함.
    • 주기적으로 데이터베이스 성능을 모니터링필요
    • 오랜 기간 동안 사용되지 않거나 비효율적인 인덱스를 점검하여 제거하는 인덱스 최적화 작업을 수행해야함

2. 인덱스 크기 관리

  • 대규모 데이터셋에서는 인덱스 자체의 크기가 커져 디스크를 많이 차지하고 메모리 사용량도 증가시킴
  • 해결 방법
    • 부분 인덱스(Partial Index/Filtered Index)
      • 특정 조건의 데이터에만 인덱스를 생성하여 인덱스 크기를 줄임
      • 일부 DBMS에서만 지원(PostgreSQL / SQL Server / SQLite 등)
    • 커버링 인덱스(Covering Index)
      • 필요한 컬럼을 모두 포함하여 인덱스로 만들어서 조회함
      • 테이블 접근을 줄여 성능을 향상시키지만 커버링 인덱스 크기가 커질수도 있음.

3. 카디널리티(Cardinality) 고려

  • 카디널리티는 컬럼이 가지는 고유 값의 개수” 또는 “데이터의 분포 다양성”
  • 카디널리티가 높을수록 인덱스 성능이 좋아짐
  • 예시
    • 기준 테이블 user(id, email, gender, status, created_at)
    • gender (낮은 카디널리티)
      • 전체 100만 건이어도 값은 2개뿐
      • 인덱스 걸어도 대부분 “반 정도” 읽어야 함
      • 거의 필터링 효과 없음
    • email (높은 카디널리티)
      • aaa@test.com, bbb@test.com, ccc@test.com
      • 거의 전부 유니크로 인덱스 걸면 바로 1건으로 좁혀짐

4. 쿼리 조건에 적합한 인덱스 설계

  • 기본 원칙은 쿼리에서 가장 자주 사용되는 조건절에 맞는 인덱스를 설계해야 함
  • 복합 인덱스 설계 시
    • 카디널리티가 높은 컬럼을 인덱스의 가장 앞쪽에 배치하는 것이 일반적인 권장 사항
    • 인덱스가 가장 먼저 가장 많은 양의 데이터를 효과적으로 필터링할 수 있도록 도움
    • (user_id, updated_at) 복합 인덱스에서
      • user_id의 카디널리티가 updated_at보다 훨씬 높다면,
      • user_id를 먼저 두는 것이 좋음
This post is licensed under CC BY 4.0 by the author.