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)
);
효율적인 쿼리 (PARENT_ID, CHILD_ID 순서)
SELECT * FROM CHILD WHERE PARENT_ID = 'A';
- PARENT_ID가 첫 번째 컬럼이므로, 인덱스를 빠르게 탐색 가능.
- 인덱스 스캔 범위가 제한되므로 성능이 향상됨.
비효율적인 쿼리 (CHILD_ID, PARENT_ID 순서)
SELECT * FROM CHILD WHERE PARENT_ID = 'A';
- 인덱스가 (CHILD_ID, PARENT_ID) 순서라면,
PARENT_ID
만으로는 인덱스를 활용할 수 없음. - 결국 풀 테이블 스캔(full table scan) 이 발생할 가능성이 높음.
인덱스 컬럼 배치 전략
-
카디널리티(Cardinality, 고유 값 개수)가 높은 컬럼을 앞에 배치
- 고유 값이 많으면 검색 범위를 빠르게 좁힐 수 있음.
-
자주
=
(Equal) 조건으로 검색되는 컬럼을 앞에 배치- 필터링을 우선적으로 수행하여 효율적인 인덱스 사용 가능.
-
범위 조건(
<
,>
,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;
}
결과: 인덱스가 (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;
}
결과: 동일하게 (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;
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. 성능 최적화를 위한 설계 가이드
-
쿼리 패턴 분석
- 어떤 컬럼이
=
조건으로 자주 사용되는지 확인. - 범위 조건(
BETWEEN
,LIKE
)을 포함하는 컬럼을 분석.
- 어떤 컬럼이
-
카디널리티 고려
- 고유 값이 많은 컬럼을 앞에 배치하여 인덱스 효율 극대화.
-
실행 계획(EXPLAIN) 확인
- 실제로 인덱스가 적절히 사용되는지 점검.
7. 결론
- 복합 키의 인덱스 순서는 쿼리 성능 최적화의 핵심 요소
- 카디널리티가 높은 컬럼, 자주 사용되는 필터 컬럼을 먼저 배치
-
JPA에서
@IdClass
또는@EmbeddedId
사용 시 필드 순서 주의 - 실행 계획(EXPLAIN)을 활용하여 최적화된 인덱스 설계 필요
이 가이드를 통해 JPA에서 복합 키를 효과적으로 관리하고 쿼리 성능을 최적화해보자.
Top comments (0)