파티셔닝 — 대용량 테이블을 나누는 전략
테이블에 행이 수억 개가 되면 인덱스를 걸어도 느려집니다. 테이블 자체를 나눌 수는 없을까요?
파티셔닝이란
파티셔닝(Partitioning)은 하나의 논리적 테이블을 파티션 키 기준으로 여러 물리적 조각(파티션)으로 나누는 기능입니다.
orders 테이블 (파티셔닝 전)
┌───────────────────────────┐
│ 2023년 데이터 + 2024년 + 2025년 │ ← 전체를 스캔해야 함
└───────────────────────────┘
orders 테이블 (연도별 RANGE 파티셔닝)
┌──────────┬──────────┬──────────┐
│ p_2023 │ p_2024 │ p_2025 │ ← 해당 파티션만 스캔
└──────────┴──────────┴──────────┘
애플리케이션 입장에서는 여전히 하나의 테이블로 보입니다. SQL을 변경할 필요가 없습니다.
파티셔닝의 종류
RANGE 파티셔닝
컬럼 값의 범위로 파티션을 나눕니다. 날짜 기반 파티셔닝에 가장 많이 사용됩니다.
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT,
customer_id INT,
amount DECIMAL(10,2),
created_at DATE,
PRIMARY KEY (id, created_at) -- 파티션 키가 PK에 포함되어야 함
) PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p_2023 VALUES LESS THAN (2024),
PARTITION p_2024 VALUES LESS THAN (2025),
PARTITION p_2025 VALUES LESS THAN (2026),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
-- 이 쿼리는 p_2025 파티션만 스캔 (파티션 프루닝)
SELECT * FROM orders WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01';
-- 오래된 데이터 삭제가 빠름 (파티션 단위로 DROP)
ALTER TABLE orders DROP PARTITION p_2023;
-- DELETE보다 훨씬 빠르고 Undo 로그가 생기지 않음
RANGE COLUMNS
함수 없이 컬럼 값 자체로 범위를 지정합니다. 여러 컬럼도 사용 가능합니다.
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT,
created_at DATE,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE COLUMNS (created_at) (
PARTITION p_2023 VALUES LESS THAN ('2024-01-01'),
PARTITION p_2024 VALUES LESS THAN ('2025-01-01'),
PARTITION p_2025 VALUES LESS THAN ('2026-01-01'),
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
LIST 파티셔닝
특정 값 목록으로 파티션을 나눕니다.
CREATE TABLE logs (
id BIGINT AUTO_INCREMENT,
level VARCHAR(10),
message TEXT,
created_at DATETIME,
PRIMARY KEY (id, level)
) PARTITION BY LIST COLUMNS (level) (
PARTITION p_error VALUES IN ('ERROR', 'FATAL'),
PARTITION p_warn VALUES IN ('WARN'),
PARTITION p_info VALUES IN ('INFO', 'DEBUG', 'TRACE')
);
HASH 파티셔닝
해시 함수를 사용하여 데이터를 균등하게 분배합니다.
CREATE TABLE sessions (
id BIGINT AUTO_INCREMENT,
user_id INT,
data TEXT,
PRIMARY KEY (id, user_id)
) PARTITION BY HASH (user_id)
PARTITIONS 8;
-- user_id % 8 값으로 파티션 결정
데이터가 균등하게 분배되지만, 범위 쿼리에서는 프루닝이 되지 않습니다.
KEY 파티셔닝
MySQL의 내부 해시 함수를 사용합니다. HASH와 유사하지만 문자열 등 다양한 타입을 지원합니다.
CREATE TABLE data (
id BIGINT AUTO_INCREMENT,
name VARCHAR(100),
PRIMARY KEY (id, name)
) PARTITION BY KEY (name)
PARTITIONS 4;
파티션 프루닝 (Partition Pruning)
파티셔닝의 핵심 성능 이점입니다. 옵티마이저가 WHERE 조건을 보고 불필요한 파티션을 건너뜁니다.
-- RANGE(YEAR(created_at))로 파티셔닝된 테이블
EXPLAIN SELECT * FROM orders WHERE created_at = '2025-06-15';
-- partitions: p_2025 ← p_2025만 스캔
EXPLAIN SELECT * FROM orders WHERE created_at >= '2024-06-01';
-- partitions: p_2024,p_2025,p_future ← 해당 범위의 파티션만 스캔
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;
-- partitions: p_2023,p_2024,p_2025,p_future ← 프루닝 불가! 전체 파티션 스캔
프루닝이 동작하려면 WHERE 절에 파티션 키가 포함되어야 합니다.
파티션 관리
파티션 추가
-- RANGE에서 새 파티션 추가 (MAXVALUE 파티션이 있으면 REORGANIZE 필요)
ALTER TABLE orders REORGANIZE PARTITION p_future INTO (
PARTITION p_2026 VALUES LESS THAN (2027),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
파티션 삭제
-- 오래된 파티션 삭제 (데이터도 함께 삭제됨)
ALTER TABLE orders DROP PARTITION p_2023;
파티션 DROP은 DELETE보다 훨씬 빠릅니다. 인덱스 업데이트나 Undo 로그 생성이 없기 때문입니다.
파티션 분할
-- 하나의 파티션을 둘로 나눔
ALTER TABLE orders REORGANIZE PARTITION p_2025 INTO (
PARTITION p_2025_h1 VALUES LESS THAN (2025.5),
PARTITION p_2025_h2 VALUES LESS THAN (2026)
);
파티션 병합
-- 두 파티션을 하나로 합침
ALTER TABLE orders REORGANIZE PARTITION p_2025_h1, p_2025_h2 INTO (
PARTITION p_2025 VALUES LESS THAN (2026)
);
파티셔닝의 제약사항
MySQL 파티셔닝에는 중요한 제약이 있습니다.
1. 파티션 키가 PK/UK에 포함되어야 함
-- 에러! 파티션 키(created_at)가 PK에 없음
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
created_at DATE
) PARTITION BY RANGE (YEAR(created_at)) (...);
-- 정상: 파티션 키를 PK에 포함
CREATE TABLE orders (
id BIGINT,
created_at DATE,
PRIMARY KEY (id, created_at) -- created_at 포함
) PARTITION BY RANGE (YEAR(created_at)) (...);
이 제약 때문에 id 단독 PK를 사용할 수 없게 되어 애플리케이션 설계에 영향을 줄 수 있습니다.
2. 외래 키 사용 불가
파티셔닝된 테이블에서는 외래 키를 정의할 수 없습니다.
3. 최대 파티션 수
테이블당 최대 8192개 파티션을 생성할 수 있습니다.
4. 사용 가능한 함수 제한
파티션 표현식에서 사용할 수 있는 함수가 제한됩니다. YEAR(), TO_DAYS(), TO_SECONDS(), UNIX_TIMESTAMP() 등 일부 함수만 사용 가능합니다.
파티셔닝 vs 샤딩
| 구분 | 파티셔닝 | 샤딩 |
|---|---|---|
| 분할 범위 | 단일 서버 내 | 여러 서버에 걸쳐 |
| 투명성 | SQL 변경 불필요 | 애플리케이션 또는 미들웨어 필요 |
| 확장성 | 단일 서버 한계 | 서버 추가로 수평 확장 |
| 복잡도 | 낮음 | 높음 (분산 트랜잭션, 조인 등) |
파티셔닝은 단일 서버의 한계 내에서 성능을 개선하는 방법이고, 샤딩은 그 한계를 넘어서기 위한 방법입니다.
파티셔닝이 효과적인 경우
-
시계열 데이터: 로그, 이벤트, 주문 등 시간순 데이터
- 오래된 데이터 삭제가 빈번 (파티션 DROP)
- 최근 데이터 위주로 조회 (프루닝)
-
대용량 테이블의 관리 작업
- 파티션 단위로
OPTIMIZE TABLE수행 - 파티션 단위로 백업/복원
- 파티션 단위로
-
특정 범위의 데이터만 자주 조회하는 경우
- 프루닝으로 불필요한 스캔 방지
파티셔닝이 효과적이지 않은 경우
- 파티션 키가 WHERE 절에 포함되지 않는 쿼리가 많을 때
- 테이블 크기가 작을 때 (파티셔닝 오버헤드만 증가)
- 대부분의 쿼리가 PK로 단일 행을 조회할 때 (이미 충분히 빠름)
정리
- 파티셔닝은 큰 테이블을 파티션 키 기준으로 나누어 특정 파티션만 스캔하게 하는 기능입니다
- RANGE 파티셔닝이 가장 많이 사용되며, 날짜 기반 데이터에 효과적입니다
- 파티션 프루닝이 동작하려면 WHERE 절에 파티션 키가 포함되어야 합니다
- 파티션 키는 반드시 PK/UK에 포함되어야 하는 제약이 있습니다
- 오래된 데이터 삭제 시 파티션 DROP은 DELETE보다 압도적으로 빠릅니다