DB Study

11장. 파티셔닝과 병렬 처리

10억 건짜리 테이블을 다루는 두 가지 무기 — 읽을 범위를 잘라내는 파티셔닝, 읽는 손을 늘리는 병렬 처리. 대용량 Oracle 운영의 양대 축입니다.

고급 ⏱ 약 30분 🎬 애니메이션 3개 선수 지식: 9~10장
🧭
이 장에서 배우는 것

① Range/List/Hash/Composite 파티셔닝의 선택 기준 ② Partition Pruning으로 스캔 범위를 잘라내는 원리 ③ Local vs Global 인덱스와 파티션 유지보수(DROP/EXCHANGE) ④ Parallel Query의 PX 서버·granule 동작과 병렬의 비용

11.1 파티셔닝 — 하나의 테이블, 여러 개의 서랍

파티셔닝은 논리적으로는 하나인 테이블을 물리적으로 여러 조각(파티션 = 각각 독립된 세그먼트)으로 나누어 저장하는 기능입니다(Enterprise Edition 옵션). 응용 프로그램은 여전히 테이블 하나로 보고 쿼리하지만, Oracle은 내부적으로 필요한 서랍만 열 수 있게 됩니다.

방식나누는 기준대표 용도
Range값의 범위 (VALUES LESS THAN)날짜·기간 데이터. 가장 널리 쓰임. 오래된 파티션 DROP으로 보관주기 관리
List명시한 값 목록지역 코드, 국가, 지점처럼 이산적인 값
Hash해시 함수로 균등 분산자연스러운 기준이 없을 때 I/O·경합 분산 (파티션 수는 2의 거듭제곱 권장)
Composite2단 조합 (파티션 → 서브파티션)Range-Hash(월별 + 고객 분산), Range-List(월별 + 지역별) 등
IntervalRange의 자동화 버전새 범위의 데이터가 들어오면 파티션을 자동 생성 (11g+)
-- 월별 Range 파티셔닝의 전형
CREATE TABLE sales (
  sale_id   NUMBER,
  sale_date DATE NOT NULL,
  amount    NUMBER(12,2)
)
PARTITION BY RANGE (sale_date) (
  PARTITION p2026_01 VALUES LESS THAN (DATE '2026-02-01'),
  PARTITION p2026_02 VALUES LESS THAN (DATE '2026-03-01'),
  PARTITION p2026_03 VALUES LESS THAN (DATE '2026-04-01')
  -- ... 월마다 계속
);

11.2 Partition Pruning — 안 읽는 것이 가장 빠르다

파티셔닝의 성능 효과는 대부분 pruning(가지치기)에서 나옵니다. WHERE 절에 파티션 키 조건이 있으면 옵티마이저는 대상이 아닌 파티션을 아예 접근 대상에서 제외합니다. 실행계획의 Pstart/Pstop 컬럼에서 몇 번 파티션만 읽는지 확인할 수 있습니다.

SELECT SUM(amount) FROM sales WHERE sale_date >= DATE '2026-03-01' AND sale_date < DATE '2026-04-01' p2026_01 1월 · 8천만행 p2026_02 2월 · 8천만행 p2026_03 3월 · 8천만행 p2026_04 4월 · 8천만행 p2026_05 5월 · 8천만행 … p2026_12 이하 7개 🧠 파티션 키 조건 발견 → 경계와 비교 Pstart = 3, Pstop = 3 ✔ 이 서랍만 스캔 pruned ✂ pruned ✂ pruned ✂ pruned ✂ pruned ✂ 스캔량 9.6억 → 8천만 행 (1/12) — 인덱스 없이도 조건만으로 I/O 92% 절감 단, TRUNC(sale_date) 처럼 파티션 키를 가공하면 pruning 실패 → 12개 전부 스캔
🔀
다른 DB와 비교하며 읽기

PostgreSQL도 선언적 파티셔닝과 pruning을 지원하지만(PG 12장 파티셔닝) Oracle처럼 EXCHANGE 같은 풍부한 유지보수 명령은 제한적입니다. ClickHouse의 파티션(CH 5장)은 MergeTree의 물리 디렉터리 단위로, "파티션 DROP으로 보관주기 관리"라는 운영 패턴은 세 DB가 놀랍도록 닮았습니다.

11.3 Local vs Global 인덱스

파티션 테이블의 인덱스는 두 종류입니다. 이 선택이 유지보수 편의성을 좌우합니다.

🗂 Local Index

테이블과 같은 기준으로 잘린 인덱스 — 파티션마다 자기만의 작은 인덱스를 가집니다. 파티션을 DROP/EXCHANGE해도 해당 조각만 함께 처리되어 유지보수가 깔끔합니다. DW·이력성 테이블의 기본 선택.

🌐 Global Index

파티션 경계와 무관하게 테이블 전체를 하나로 커버하는 인덱스(자체적으로 다르게 파티셔닝할 수도 있음). 파티션 키가 조건에 없는 조회(예: PK 단건 조회)에 유리하지만, 파티션 DDL 시 인덱스가 통째로 UNUSABLE이 될 수 있습니다.

-- local: 파티션마다 하나씩, 테이블과 같은 기준으로 분할
CREATE INDEX sales_cust_ix ON sales(cust_id) LOCAL;

-- 파티션 DDL 시 global 인덱스를 살리려면 UPDATE INDEXES를 붙인다 (대신 DDL이 느려짐)
ALTER TABLE sales DROP PARTITION p2025_01 UPDATE INDEXES;

11.4 파티션 유지보수 — DROP과 EXCHANGE

파티셔닝의 진짜 매력은 조회보다 운영에 있습니다. 1억 건 삭제를 DELETE로 하면 몇 시간 + 대량 undo/redo가 들지만, DROP PARTITION은 딕셔너리 작업 수준으로 끝납니다. 대량 적재의 비밀 병기는 EXCHANGE PARTITION입니다.

🏛 운영 테이블 sales (서비스 중) p2026_05 5월 데이터 p2026_06 6월 데이터 p2026_07 (빈 파티션) 🚚 임시 테이블 sales_stage 7월 신규 1억 건 적재 완료 + 인덱스·통계까지 준비 서비스와 무관한 곳에서 느긋하게 작업 ALTER TABLE sales EXCHANGE PARTITION p2026_07 WITH TABLE sales_stage; 딕셔너리의 세그먼트 포인터만 맞교환! p2026_07 7월 1억 건 ✔ 데이터 블록은 1바이트도 이동하지 않음 → 1억 건 적재가 사실상 즉시 완료 임시 테이블은 빈 파티션과 교환되어 빈 껍데기가 됨 — DW 야간 적재의 표준 패턴

11.5 Parallel Query — PX 서버와 granule

파티셔닝이 "읽을 양"을 줄인다면, 병렬 처리(Parallel Execution)는 "읽는 속도"를 늘립니다. 세션의 서버 프로세스가 QC(Query Coordinator)가 되고, PX 서버라는 일꾼 프로세스들이 작업을 나눠 맡습니다. 동시에 일하는 일꾼 수가 DOP(Degree of Parallelism)입니다.

작업 분배 단위는 granule입니다. 풀스캔이라면 테이블을 블록 범위 granule로 잘게 나누고, PX 서버들이 하나씩 가져가 처리한 뒤 끝나면 다음 것을 가져갑니다 — 먼저 끝난 일꾼이 놀지 않는 동적 분배입니다(파티션 단위 granule이 쓰이는 연산도 있습니다).

QC (조정자) DOP = 4 결정 PX 서버 P001 PX 서버 P002 PX 서버 P003 PX 서버 P004 테이블 10억 행 · 연속된 블록들 블록 범위 granule 8개로 분할 4명이 동시에 스캔 — 끝낸 일꾼은 남은 granule을 가져감 (동적 분배) 부분 결과를 QC로 취합 → 이론상 최대 1/DOP 시간, 대신 CPU·I/O를 4배 소비
-- 문장 단위로 병렬 지시 (가장 안전한 사용법)
SELECT /*+ PARALLEL(s 8) */ SUM(amount)
FROM   sales s
WHERE  sale_date >= DATE '2026-01-01';

-- 실행계획에는 PX COORDINATOR / PX BLOCK ITERATOR 등이 나타난다

11.6 병렬의 비용 — 언제 쓰고 언제 참는가

병렬 처리는 한 쿼리가 시스템 자원을 독식하도록 허가하는 것입니다. DOP 8이면 CPU 코어 8개와 8배의 I/O 대역폭을 그 쿼리 하나가 씁니다. 야간 배치·DW 집계처럼 "이 쿼리가 지금 시스템의 주인"일 때는 훌륭하지만, 동시 사용자가 많은 OLTP 낮 시간대에 남용하면 전체 서비스가 함께 느려집니다.

⚠️
테이블 속성으로 PARALLEL을 박아두지 말 것

ALTER TABLE ... PARALLEL 8처럼 테이블 속성으로 병렬도를 설정하면 그 테이블을 읽는 모든 쿼리가 병렬 후보가 됩니다. 낮 시간 OLTP 쿼리까지 PX 서버를 삼키는 사고의 단골 원인입니다. 병렬이 필요한 문장에만 힌트로 지정하는 것이 안전합니다.

✍️ 이해도 체크

월별 Range 파티션 테이블에 WHERE TO_CHAR(sale_date,'YYYYMM') = '202603' 조건으로 조회했더니 매우 느립니다. 원인은?
✅ 옵티마이저는 가공되지 않은 파티션 키와 경계값을 비교해야 pruning할 수 있습니다. sale_date >= DATE '2026-03-01' AND sale_date < DATE '2026-04-01'처럼 컬럼을 그대로 두는 범위 조건으로 바꾸면 해당 월 파티션만 읽습니다.
EXCHANGE PARTITION으로 1억 건을 운영 테이블에 넣는 작업이 순식간에 끝나는 이유는?
✅ EXCHANGE는 임시 테이블의 세그먼트와 파티션의 세그먼트가 서로 "명찰"을 바꾸는 메타데이터 연산입니다. 데이터 블록은 제자리에 그대로 있으므로 데이터 양과 무관하게 빠릅니다. 적재 자체(임시 테이블 채우기)는 미리 서비스 밖에서 해두는 것이 이 패턴의 핵심입니다.
📚
원문으로 더 깊이

Oracle Database 19c 공식 문서: VLDB and Partitioning Guide — Partitioning Concepts, Introduction to Parallel Execution