Skip to content
난이도 문제은행 5

파티션 2 ​

파티션에 이어 파티션(partition)을 설계할 때 걸리는 제약과 종류, 성능 함정을 다룬다. 제약은 대부분 "파티션마다 인덱스를 따로 가진다"는 한 가지 사실에서 나온다. 그래서 파티셔닝(partitioning)은 쿼리를 빠르게 하는 만능 도구가 아니라, 데이터를 조각 단위로 관리하려고 쓰는 도구에 가깝다.

제약 1: 파티션 키는 모든 유니크 인덱스에 들어가야 한다 ​

행이 들어갈 파티션을 정하는 칼럼을 파티션 키(partition key)라 한다. MySQL 매뉴얼의 규칙은 이렇다. 파티션 식(PARTITION BY 뒤에 쓰는 칼럼이나 식, 예: YEAR(created_at))에 쓰인 칼럼은 테이블의 모든 유니크 키에 포함되어야 한다. 기본 키(primary key)도 유니크 키이므로 이 규칙을 따른다. 테이블에 유니크 인덱스(unique index)가 하나도 없을 때만 이 제약이 없다.

이유는 인덱스가 파티션마다 따로 있기 때문이다. email에 유니크 인덱스를 걸고 테이블을 가입일로 나눴다고 해 보자. 같은 이메일이 9월 파티션과 10월 파티션에 들어오면, 각 파티션의 인덱스는 자기 안만 보므로 중복을 알아채지 못한다. 유니크 키에 파티션 키가 들어 있으면, 같은 키 값을 가진 행은 늘 같은 파티션에 가므로 파티션 하나 안에서만 검사해도 된다.

sql
-- 에러: 파티션 키 created_at이 기본 키 (id)에 없다
CREATE TABLE t (id BIGINT PRIMARY KEY, created_at DATETIME NOT NULL)
PARTITION BY RANGE COLUMNS (created_at) (PARTITION p0 VALUES LESS THAN ('2027-01-01'));

그래서 날짜로 나눈 테이블은 기본 키를 (id, created_at)처럼 만든다. 그 결과 "id만으로 유일"이나 "이메일은 전체에서 유일" 같은 규칙은 DB가 지켜 주지 못한다. 이런 보장이 꼭 필요하면 파티션을 쓰지 않거나, 유일성 검사를 다른 테이블이나 애플리케이션에서 해야 한다.

제약 2: 그 밖에 알아 둘 것 ​

파티션을 지원하는 스토리지 엔진(storage engine)은 정해져 있다. 또 InnoDB 파티션 테이블은 외래 키(foreign key)를 쓸 수 없으므로, 없는 부모를 가리키는 행을 막는 참조 무결성(referential integrity)을 애플리케이션이 지켜야 한다(외래키와 잠금 대기 참고). 세션 동안만 쓰는 임시 테이블(temporary table)도 파티셔닝할 수 없다.

제약내용(MySQL 8.4)
스토리지 엔진InnoDB와 NDB(MySQL Cluster용 엔진)만 지원. MyISAM 테이블은 파티셔닝할 수 없다
외래 키InnoDB 파티션 테이블은 외래 키를 가질 수도, 참조될 수도 없다
인덱스·타입FULLTEXT 인덱스, 공간 데이터 칼럼 불가
임시 테이블파티셔닝할 수 없다
파티션 수서브파티션(subpartition)까지 합쳐 최대 8,192개
파티션 식정수(또는 정수가 되는 식). 칼럼 값을 그대로 받는 KEY·COLUMNS 계열만 예외

파티션을 만든 뒤에는 서버 SQL 모드(SQL mode)를 바꾸지 말라는 매뉴얼 경고도 있다. SQL 모드는 서버가 SQL을 얼마나 엄격하게 해석할지 정하는 설정인데, 이것이 바뀌면 파티션 식의 계산 결과가 달라져 테이블을 못 쓰게 될 수 있다.

파티션 종류 ​

종류행을 나누는 기준어울리는 데이터
RANGE값의 구간(LESS THAN)날짜별 이력·로그
LIST값의 목록(IN (…))지역·상태 코드
RANGE·LIST COLUMNS칼럼 값 그대로(날짜·문자열, 여러 칼럼)날짜를 함수 없이 나눌 때
HASH사용자 식 값을 파티션 수로 나눈 나머지고르게 흩뿌리기
KEYMySQL 내부 해시 함수정수가 아닌 칼럼 흩뿌리기
  • RANGE가 가장 흔하다. 날짜로 나누고 오래된 구간을 버리는 패턴이 파티셔닝의 대표 용도다. RANGE는 정수 식만 받으므로 날짜는 YEAR(created_at)이나 TO_DAYS(created_at)처럼 정수로 바꾼다. RANGE COLUMNS를 쓰면 DATE·DATETIME 칼럼을 함수 없이 바로 쓸 수 있다.
  • HASH는 MOD(식, 파티션 수)로 파티션을 정한다. 구간 개념이 없으므로 오래된 데이터를 파티션째 버리는 용도에는 맞지 않는다.
  • KEY는 HASH와 같은 방식이지만 해시 함수를 MySQL이 제공한다. 그래서 문자열처럼 정수가 아닌 칼럼도 쓸 수 있다(TEXT·BLOB 제외). PARTITION BY KEY()처럼 칼럼을 비우면 기본 키를 쓴다.
  • LINEAR HASH·LINEAR KEY는 나머지 대신, 파티션 수 이상인 가장 작은 2의 거듭제곱을 기준으로 값의 비트를 잘라 파티션을 고른다. 이렇게 하면 파티션을 추가·삭제·병합·분할할 때 옮길 데이터가 적어 관리가 훨씬 빠르다. 대신 일반 HASH보다 데이터가 덜 고르게 퍼질 수 있다.
  • 서브파티션은 파티션을 한 번 더 나누는 것이다. RANGE·LIST 파티션만 HASH·KEY로 다시 나눌 수 있다.

쿼리 성능 주의 ​

파티셔닝이 오히려 쿼리를 느리게 하는 경우도 있다.

  • 파티션 키 조건이 없는 쿼리는 모든 파티션을 훑는다. 예를 들어 날짜로 나눈 테이블에서 user_id로만 찾으면, user_id 세컨더리 인덱스(secondary index)가 파티션마다 따로 있으므로 파티션 수만큼 인덱스를 따로 탐색한다. 파티션이 없는 테이블이라면 인덱스 한 번이면 끝날 일이다.
  • 작은 테이블이나 한 건 조회 위주의 테이블에는 이득이 거의 없다. 이런 쿼리는 인덱스가 이미 필요한 행만 짚어 주므로, 먼저 인덱스를 설계하는 편이 낫다(B-Tree 활용 참고).
  • 파티션이 너무 많으면 관리 비용이 커진다. 파티션마다 파일과 메타데이터가 따로 있어 열어야 할 파일이 늘어난다. 매뉴얼도 파일을 여는 도중 자원 부족 에러가 나면, 서버가 동시에 열 수 있는 파일 수의 상한인 open_files_limit을 늘리라고 안내한다.
  • 파티셔닝은 샤딩(sharding)이 아니다. 샤딩은 데이터를 여러 서버에 나눠 담는 것이다. 파티션은 모두 같은 서버의 같은 자원을 쓰므로 서버 한 대의 CPU·메모리 한계를 넘게 해 주지는 않는다.

면접 포인트 ​

  • "파티션 테이블에서 유니크 키 제약은?" → 파티션 키 칼럼이 모든 유니크 키(기본 키 포함)에 들어가야 한다. 인덱스가 파티션별이라 파티션 간 중복을 검사할 수 없기 때문.
  • "파티션을 쓰면 무조건 빨라지나요?" → 아니다. 파티션 키 조건이 없으면 파티션 수만큼 탐색. 이득은 조각 단위 삭제와 파티션 프루닝(partition pruning).
  • "외래 키와 같이 쓸 수 있나요?" → InnoDB 파티션 테이블은 외래 키 미지원.
  • RANGE는 날짜 이력, HASH·KEY는 고른 분산. LINEAR는 관리가 빠르고 분포가 덜 고르다.

관련 문서