DEV Community

junghwan
junghwan

Posted on

JPA에서 복합 키(Composite Key)와 인덱스 순서 최적화: 성능 향상을 위한 실전 가이드

JPA에서 복합 키(Composite Key)와 인덱스 순서 최적화: 성능 향상을 위한 실전 가이드


JPA에서 복합 키와 인덱스 순서 최적화

JPA를 활용한 엔티티 매핑에서 복합 키(Composite Key)의 인덱스 순서는 쿼리 성능에 직접적인 영향을 미친다. 본 글에서는 복합 키 인덱스의 동작 방식과 JPA에서 이를 효과적으로 관리하는 방법을 다룬다.


1. 복합 키와 기본 키 인덱스

복합 키란?

복합 키(Composite Key)는 두 개 이상의 컬럼을 조합하여 레코드를 고유하게 식별하는 키다. 예를 들어, CHILD 테이블에서 (PARENT_ID, CHILD_ID)를 복합 키로 설정하면 각 레코드가 고유하게 관리된다.

기본 키 인덱스의 특징

  • 고유성(Unique): 중복 값을 허용하지 않는다.
  • NOT NULL: 기본 키는 NULL 값을 허용하지 않는다.
  • 클러스터형 vs. 비클러스터형 인덱스
    • MySQL(InnoDB), SQL Server클러스터형 인덱스: 데이터가 기본 키 순서대로 물리적으로 저장됨.
    • Oracle비클러스터형 인덱스: 인덱스가 데이터의 물리적 저장 순서와 무관하게 동작함.

2. 인덱스 순서가 성능에 미치는 영향

B-트리 인덱스는 왼쪽에서 오른쪽으로 조건을 평가하기 때문에 복합 키의 컬럼 순서는 성능에 큰 영향을 미친다.

인덱스 컬럼 순서에 따른 성능 차이

CREATE TABLE CHILD (
    PARENT_ID VARCHAR(20),
    CHILD_ID VARCHAR(20),
    NAME VARCHAR(255),
    PRIMARY KEY (PARENT_ID, CHILD_ID)
);
Enter fullscreen mode Exit fullscreen mode

효율적인 쿼리 (PARENT_ID, CHILD_ID 순서)

SELECT * FROM CHILD WHERE PARENT_ID = 'A';
Enter fullscreen mode Exit fullscreen mode
  • PARENT_ID가 첫 번째 컬럼이므로, 인덱스를 빠르게 탐색 가능.
  • 인덱스 스캔 범위가 제한되므로 성능이 향상됨.

비효율적인 쿼리 (CHILD_ID, PARENT_ID 순서)

SELECT * FROM CHILD WHERE PARENT_ID = 'A';
Enter fullscreen mode Exit fullscreen mode
  • 인덱스가 (CHILD_ID, PARENT_ID) 순서라면, PARENT_ID만으로는 인덱스를 활용할 수 없음.
  • 결국 풀 테이블 스캔(full table scan) 이 발생할 가능성이 높음.

인덱스 컬럼 배치 전략

  1. 카디널리티(Cardinality, 고유 값 개수)가 높은 컬럼을 앞에 배치
    • 고유 값이 많으면 검색 범위를 빠르게 좁힐 수 있음.
  2. 자주 =(Equal) 조건으로 검색되는 컬럼을 앞에 배치
    • 필터링을 우선적으로 수행하여 효율적인 인덱스 사용 가능.
  3. 범위 조건(<, >, BETWEEN, LIKE)이 포함된 컬럼은 뒤에 배치
    • 앞쪽에 위치하면 인덱스 탐색이 비효율적일 수 있음.

3. JPA에서 복합 키 매핑

JPA에서 복합 키는 @IdClass@EmbeddedId를 사용해 정의할 수 있다.

3.1 @IdClass 방식

// 식별자 클래스
public class ChildId implements Serializable {
    private String parent;  // 첫 번째 필드 (PARENT_ID)
    private String childId; // 두 번째 필드 (CHILD_ID)
}

// 엔티티 클래스
@Entity
@IdClass(ChildId.class)
public class Child {
    @Id
    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Parent parent;

    @Id
    @Column(name = "CHILD_ID")
    private String childId;

    private String name;
}
Enter fullscreen mode Exit fullscreen mode

결과: 인덱스가 (PARENT_ID, CHILD_ID) 순서로 생성됨.


3.2 @EmbeddedId 방식

@Embeddable
public class ChildId implements Serializable {
    @Column(name = "PARENT_ID")
    private String parent;  // 첫 번째 필드

    @Column(name = "CHILD_ID")
    private String childId; // 두 번째 필드
}

@Entity
public class Child {
    @EmbeddedId
    private ChildId id;

    private String name;
}
Enter fullscreen mode Exit fullscreen mode

결과: 동일하게 (PARENT_ID, CHILD_ID) 순서로 인덱스가 생성됨.


4. 실전 예제: 복합 키와 쿼리 성능 최적화

4.1 자주 실행되는 쿼리

SELECT * FROM CHILD WHERE PARENT_ID = 'A';
SELECT * FROM CHILD WHERE PARENT_ID = 'A' AND CHILD_ID = 'B';
SELECT * FROM CHILD WHERE PARENT_ID = 'A' ORDER BY CHILD_ID;
Enter fullscreen mode Exit fullscreen mode

4.2 최적의 인덱스 순서: (PARENT_ID, CHILD_ID)

  • PARENT_ID가 = 조건으로 사용되므로 첫 번째 컬럼.
  • CHILD_ID는 정렬 및 추가 필터링을 위해 두 번째 컬럼.

4.3 비효율적인 순서: (CHILD_ID, PARENT_ID)

  • PARENT_ID로 검색할 때 인덱스를 제대로 활용할 수 없음.
  • 인덱스 전체를 스캔해야 할 가능성이 커짐.

5. 데이터베이스별 인덱스 동작 차이

데이터베이스 인덱스 유형 성능 영향
MySQL (InnoDB) 클러스터형 인덱스 기본 키 순서대로 데이터 저장, 삽입/조회 최적화 필요
SQL Server 클러스터형 인덱스 순서 중요, 잘못된 순서는 인덱스 스캔 증가
Oracle 비클러스터형 인덱스 물리적 저장과 무관, 인덱스 탐색 효율성이 핵심

6. 성능 최적화를 위한 설계 가이드

  1. 쿼리 패턴 분석
    • 어떤 컬럼이 = 조건으로 자주 사용되는지 확인.
    • 범위 조건(BETWEEN, LIKE)을 포함하는 컬럼을 분석.
  2. 카디널리티 고려
    • 고유 값이 많은 컬럼을 앞에 배치하여 인덱스 효율 극대화.
  3. 실행 계획(EXPLAIN) 확인
    • 실제로 인덱스가 적절히 사용되는지 점검.

7. 결론

  • 복합 키의 인덱스 순서는 쿼리 성능 최적화의 핵심 요소
  • 카디널리티가 높은 컬럼, 자주 사용되는 필터 컬럼을 먼저 배치
  • JPA에서 @IdClass 또는 @EmbeddedId 사용 시 필드 순서 주의
  • 실행 계획(EXPLAIN)을 활용하여 최적화된 인덱스 설계 필요

이 가이드를 통해 JPA에서 복합 키를 효과적으로 관리하고 쿼리 성능을 최적화해보자.

Top comments (0)