Partition
파티션
대용량 테이블을 논리적으로 분할하여 쿼리 성능과 관리 효율성을 높이는 기법. Partition Pruning으로 필요한 파티션만 스캔하여 성능을 최적화합니다.
파티션
대용량 테이블을 논리적으로 분할하여 쿼리 성능과 관리 효율성을 높이는 기법. Partition Pruning으로 필요한 파티션만 스캔하여 성능을 최적화합니다.
파티션(Partition)은 대용량 테이블을 물리적 또는 논리적으로 더 작은 조각으로 분할하는 기법입니다. 하나의 테이블을 여러 파티션으로 나누어 저장하지만, 애플리케이션에서는 하나의 테이블처럼 사용합니다. 쿼리 시 WHERE 조건에 따라 필요한 파티션만 스캔하는 Partition Pruning을 통해 성능이 크게 향상됩니다.
연속된 값의 범위로 분할. 날짜, 시간, 숫자 기반 데이터에 적합.
특정 값 목록으로 분할. 이산적인 값 기반 분류에 적합.
해시 함수로 균등 분배. 특정 패턴이 없는 데이터에 적합.
| 특성 | Partition | Sharding |
|---|---|---|
| 분할 위치 | 단일 DB 인스턴스 내 | 여러 DB 인스턴스 |
| 관리 복잡도 | DB가 자동 관리 | 애플리케이션 레벨 관리 |
| 확장성 | 수직 확장 한계 | 수평 확장 가능 |
| 트랜잭션 | 전체 테이블 ACID | 분산 트랜잭션 필요 |
| 사용 케이스 | 단일 DB로 충분한 규모 | 매우 대규모 데이터 |
-- 부모 테이블 생성 (파티션 키 지정)
CREATE TABLE orders (
id SERIAL,
user_id INTEGER NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL,
PRIMARY KEY (id, created_at) -- 파티션 키 포함 필수
) PARTITION BY RANGE (created_at);
-- 월별 파티션 생성
CREATE TABLE orders_2024_01 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE orders_2024_02 PARTITION OF orders
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
CREATE TABLE orders_2024_03 PARTITION OF orders
FOR VALUES FROM ('2024-03-01') TO ('2024-04-01');
-- 기본 파티션 (범위 밖 데이터)
CREATE TABLE orders_default PARTITION OF orders DEFAULT;
-- 인덱스 생성 (파티션별 자동 적용)
CREATE INDEX idx_orders_user_id ON orders (user_id);
CREATE INDEX idx_orders_status ON orders (status);
-- Partition Pruning 동작 확인
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE created_at >= '2024-02-01' AND created_at < '2024-03-01';
-- 결과: orders_2024_02만 스캔
-- 국가별 List Partition
CREATE TABLE customers (
id SERIAL,
name VARCHAR(100) NOT NULL,
country_code CHAR(2) NOT NULL,
email VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, country_code)
) PARTITION BY LIST (country_code);
-- 국가별 파티션 생성
CREATE TABLE customers_kr PARTITION OF customers
FOR VALUES IN ('KR');
CREATE TABLE customers_asia PARTITION OF customers
FOR VALUES IN ('JP', 'CN', 'TW', 'SG');
CREATE TABLE customers_us PARTITION OF customers
FOR VALUES IN ('US', 'CA');
CREATE TABLE customers_eu PARTITION OF customers
FOR VALUES IN ('DE', 'FR', 'GB', 'IT', 'ES');
CREATE TABLE customers_other PARTITION OF customers DEFAULT;
-- 한국 고객만 조회 (자동 Pruning)
SELECT * FROM customers WHERE country_code = 'KR';
-- customers_kr 파티션만 스캔
-- 해시 기반 균등 분배
CREATE TABLE logs (
id BIGSERIAL,
user_id INTEGER NOT NULL,
action VARCHAR(50) NOT NULL,
payload JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, user_id)
) PARTITION BY HASH (user_id);
-- 4개 파티션으로 균등 분배
CREATE TABLE logs_p0 PARTITION OF logs
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE logs_p1 PARTITION OF logs
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE logs_p2 PARTITION OF logs
FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE logs_p3 PARTITION OF logs
FOR VALUES WITH (MODULUS 4, REMAINDER 3);
-- 1. 새 파티션 미리 생성 (자동화 권장)
CREATE TABLE orders_2024_04 PARTITION OF orders
FOR VALUES FROM ('2024-04-01') TO ('2024-05-01');
-- 2. 오래된 파티션 분리 (DETACH)
ALTER TABLE orders DETACH PARTITION orders_2024_01;
-- 3. 분리된 테이블 아카이브 또는 삭제
-- 옵션 A: 아카이브 테이블로 이동
ALTER TABLE orders_2024_01 RENAME TO orders_archive_2024_01;
-- 옵션 B: 데이터 삭제 (전체 파티션 즉시 삭제)
DROP TABLE orders_2024_01;
-- Full Table Scan 없이 즉시 삭제 (DELETE보다 훨씬 빠름)
-- 파티션 목록 확인
SELECT
parent.relname AS parent_table,
child.relname AS partition_name,
pg_get_expr(child.relpartbound, child.oid) AS partition_expression
FROM pg_inherits
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
WHERE parent.relname = 'orders';
-- MySQL 파티션 생성
CREATE TABLE orders (
id INT AUTO_INCREMENT,
user_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
created_at DATE NOT NULL,
PRIMARY KEY (id, created_at)
)
PARTITION BY RANGE (YEAR(created_at) * 100 + MONTH(created_at)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403),
PARTITION p202403 VALUES LESS THAN (202404),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
-- 파티션 추가
ALTER TABLE orders ADD PARTITION (
PARTITION p202404 VALUES LESS THAN (202405)
);
-- 파티션 삭제
ALTER TABLE orders DROP PARTITION p202401;
-- 파티션 정보 확인
SELECT
TABLE_NAME,
PARTITION_NAME,
PARTITION_EXPRESSION,
PARTITION_DESCRIPTION,
TABLE_ROWS
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 'orders';
Primary Key와 Unique 제약조건에 파티션 키를 반드시 포함해야 합니다.
PRIMARY KEY (id, created_at)처럼 복합 키로 정의하세요.
WHERE 절에 파티션 키 조건이 없으면 모든 파티션을 스캔합니다.
EXPLAIN으로 실제 스캔되는 파티션 수를 확인하세요. 함수로 감싼 조건(예: YEAR(created_at) = 2024)은 Pruning이 동작하지 않을 수 있습니다.
너무 많은 파티션은 오히려 성능을 저하시킵니다. PostgreSQL은 수천 개 파티션도 처리하지만, 실제 접근 패턴에 맞게 적절한 단위(월별, 분기별)로 설계하세요.