IT/프로그래밍

MySQL에서 복합 PK (Composite Primary Key)

Collin 2026. 6. 20. 08:18
반응형

MySQL에서 (name, regdate)와 같은 복합 기본키(Composite Primary Key)를 사용하는 것은 특정 비즈니스 요구사항에서는 유용할 수 있지만, 시스템이 커질수록 여러 잠재적 위험 요소성능 저하를 유발할 수 있습니다.

왜 복합 PK를 지양하고 대체로 자동 증가하는 정수형 ID(Auto-Increment ID)를 권장하는지, 장단점과 위험 요소를 나누어 명확히 정리해 드리겠습니다.

1. 잠재적 위험 요소 및 지양해야 하는 배경

① 인덱스 비대화와 메모리 낭비 (가장 큰 이유)

MySQL의 InnoDB 스토리지 엔진은 클러스터형 인덱스(Clustered Index) 구조를 사용합니다. 즉, 실제 데이터 레코드가 PK 순서대로 정렬되어 저장됩니다.

  • 더 큰 문제는 두 번째 인덱스(Secondary Index)들이 모두 이 PK 값을 포함한다는 점입니다.
  • (name, regdate)는 문자열과 날짜 타입의 조합이라 INT나 BIGINT보다 크기가 훨씬 큽니다. 만약 해당 테이블에 다른 인덱스가 3~4개 더 있다면, PK의 크기 때문에 전체 인덱스 사이즈가 비대해지고, 이는 곧 DB 서버의 RAM(Buffer Pool) 부족으로 이어져 성능이 급격히 떨어집니다.

② 페이지 분할(Page Split)로 인한 쓰기 성능 저하

InnoDB는 데이터를 페이지(기본 16KB) 단위로 저장하며, PK 순서대로 정렬합니다.

  • 만약 자동 증가(Auto-Increment) PK를 쓰면 항상 맨 뒤에 데이터가 붙으므로 효율적입니다.
  • 하지만 (name, regdate)의 경우, name이 알파벳/가나다순으로 정렬되므로 새로운 데이터가 기존 데이터 사이사이에 삽입됩니다. 이때 디스크 내부에서 공간을 확보하기 위해 데이터를 쪼개고 이동시키는 페이지 분할(Page Split) 현상이 빈번하게 발생하여 INSERT 성능이 심각하게 저하됩니다.

③ 비즈니스 로직 변경에 취약 (불변성 위배)

기본키는 데이터가 존재하는 동안 절대 변하지 않는 불변성을 가지는 것이 좋습니다.

  • 하지만 개명이나 오입력으로 인해 사용자의 name이 변경되어야 하거나, 시스템 오류로 regdate를 수정해야 하는 상황이 오면 PK 자체를 업데이트해야 합니다.
  • PK를 수정하는 것은 자식 테이블의 외래키(FK)까지 모두 흔들기 때문에 엄청난 시스템 부하와 정합성 리스크를 동반합니다.

2. 복합 PK (name, regdate)의 장단점

구분 장점 (Pros) 단점 (Cons)
성능 측면 * name과 regdate를 조건으로 하는 SELECT 쿼리 시, 추가 인덱스 없이 초고속 조회가 가능합니다.

* 별도의 대리키(Surrogate Key)를 위한 저장 공간을 아낄 수 있습니다.
* INSERT 작업 시 페이지 분할로 인해 속도가 느립니다.

* 세컨더리 인덱스들의 크기가 커져 메모리(Buffer Pool) 효율이 극도로 나빠집니다.
설계 측면 * 데이터 자체로 '동명이인이 같은 날짜에 가입할 수 없다'는 비즈니스 무결성을 데이터베이스 계층에서 강제할 수 있습니다. * name과 regdate 순서로 인덱스가 생성되므로, regdate로만 검색할 때는 복합 PK 인덱스를 제대로 타지 못합니다.

* 다른 테이블에서 이 테이블을 참조(FK)할 때 2개의 컬럼을 모두 조인해야 하므로 쿼리가 복잡해집니다.

3. 권장하는 대안 (Best Practice)

대부분의 실무에서는 이 방식을 지양하고, 대리키(Surrogate Key)와 유니크 인덱스(Unique Index)를 조합하여 해결합니다.



SQL

CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY, -- 안전하고 가벼운 대리키를 PK로 설정
    name VARCHAR(50) NOT NULL,
    regdate DATETIME NOT NULL,
    -- 비즈니스 제약조건(무결성)은 UNIQUE 인덱스로 별도 관리
    UNIQUE KEY uidx_name_regdate (name, regdate)
);

 

이 방식이 더 좋은 이유:

  1. PK 크기 최소화: PK가 BIGINT(8바이트)로 고정되므로 다른 세컨더리 인덱스들의 크기가 작아지고 메모리가 절약됩니다.
  2. 삽입 성능 최적화: 항상 마지막에 데이터가 추가되므로 페이지 분할 없이 INSERT가 매우 빠릅니다.
  3. 유연한 변경: 만약 비즈니스 요구사항이 바뀌어 동명이인의 동시 가입을 허용해야 한다면, PK를 건드릴 필요 없이 UNIQUE 인덱스만 삭제하거나 수정하면 되므로 안전합니다.

 

728x90
반응형

'IT > 프로그래밍' 카테고리의 다른 글

Builder 와 Record 비교  (0) 2026.03.29
Filter ,Interceptor , AOP  (0) 2026.03.28
[JPA/Hibernate] IN 절 파라미터 패딩(Parameter Padding)  (0) 2026.02.06
@Transactional의 동작  (0) 2025.12.13
상태 변수 이름 state, status 차이  (0) 2025.10.23