본문으로 건너뛰기
7.3 상속 기반 vs 선언적 파티션

7.3 상속 기반 vs 선언적 파티션

PostgreSQL의 파티셔닝은 두 세대가 있습니다. 상속 기반(table inheritance, ~PG 9.x)과 선언적(declarative, PG 10+)입니다. 운영 중인 옛 시스템에서 상속 파티션을 마주칠 수 있어, 둘의 차이와 마이그레이션 방법을 알아 두는 게 좋습니다.

상속 기반 — PG 9.x 패턴

PG 10 이전에는 table inheritance와 check constraint, 트리거 또는 rule을 조합해 파티셔닝을 구현했습니다.

-- 부모 (CHECK constraint 없음)
CREATE TABLE events (
  id bigserial,
  created_at timestamptz,
  payload jsonb
);

-- 자식 (INHERITS + 각자 CHECK)
CREATE TABLE events_2024_q1 (
  CHECK (created_at >= '2024-01-01' AND created_at < '2024-04-01')
) INHERITS (events);

CREATE TABLE events_2024_q2 (
  CHECK (created_at >= '2024-04-01' AND created_at < '2024-07-01')
) INHERITS (events);

-- INSERT 라우팅을 위한 트리거 (또는 rule)
CREATE FUNCTION events_insert_trigger() RETURNS TRIGGER AS $$
BEGIN
  IF NEW.created_at >= '2024-04-01' THEN
    INSERT INTO events_2024_q2 VALUES (NEW.*);
  ELSE
    INSERT INTO events_2024_q1 VALUES (NEW.*);
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER events_insert
  BEFORE INSERT ON events
  FOR EACH ROW EXECUTE FUNCTION events_insert_trigger();

문제점:

문제영향
INSERT 트리거 비용row마다 PL/pgSQL 호출 — 무거움
자동화 도구 부족새 파티션 추가·라우팅 함수 갱신 모두 수동
EXPLAIN에서 pruning 약함constraint exclusion이 작동하려면 constraint_exclusion = on (느림)
Unique·FK 제약 글로벌 불가자식마다 따로

선언적 파티션 — PG 10+

CREATE TABLE events (
  id bigserial,
  created_at timestamptz NOT NULL,
  payload jsonb,
  PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);

CREATE TABLE events_2026_05 PARTITION OF events
  FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');

차이:

항목상속 기반선언적
부모에 데이터 저장가능불가능 (스키마만)
INSERT 라우팅트리거코어 라우팅
pruningconstraint exclusion (옛 메커니즘)partition pruning (빠름)
인덱스 자동 전파안 됨PG 11+ 가능
FK 지원자식별PG 12+ 부모에 가능
TRUNCATE부모는 자식 안 비움 (CASCADE 필요)부모 TRUNCATE = 모든 자식
부분 인덱스·다중 컬럼 키자유선언적도 가능
ATTACH/DETACHINHERIT/NO INHERIT 수동ATTACH PARTITION / DETACH PARTITION 공식

어디서 상속이 여전히 유용한가

사례메모
개념적 상속 (객체 모델)한 부모를 여러 자식이 확장 — 파티셔닝 아닌 데이터 모델
매우 비균질한 자식 컬럼자식마다 컬럼 구성이 다른 폴리모픽 모델

거의 모든 파티셔닝 목적에서는 선언적이 정답.

상속 → 선언적 마이그레이션

옛 상속 기반 시스템을 선언적으로 옮기는 일은 흔합니다. 운영자가 자주 마주치는 시나리오.

절차 (요약)

  1. 새 선언적 부모 테이블 생성
  2. 자식 테이블에서 트리거·rule 제거
  3. 자식의 CHECK constraint를 명시 (선언적 ATTACH에 필요)
  4. 자식을 새 부모에 ATTACH PARTITION
  5. 옛 부모는 drop 또는 DEFAULT 파티션 대신 사용
-- 1. 새 부모
CREATE TABLE events_new (LIKE events INCLUDING ALL)
  PARTITION BY RANGE (created_at);

-- 2~3. 자식에서 INHERITS 해제 + CHECK 명시
ALTER TABLE events_2024_q1 NO INHERIT events;
ALTER TABLE events_2024_q1
  ADD CONSTRAINT events_2024_q1_created_at_check
  CHECK (created_at >= '2024-01-01' AND created_at < '2024-04-01') NOT VALID;
ALTER TABLE events_2024_q1 VALIDATE CONSTRAINT events_2024_q1_created_at_check;

-- 4. ATTACH
ALTER TABLE events_new ATTACH PARTITION events_2024_q1
  FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');

-- 5. 자식 모두 옮긴 뒤 옛 부모 정리
DROP TABLE events;
ALTER TABLE events_new RENAME TO events;

운영 중 무중단으로 하려면 트래픽을 새 부모로 점진적 전환하는 패턴이 필요합니다. 자세한 무중단 전환은 별도 작업 — 일반적으로 새 INSERT는 새 부모로, 옛 데이터는 ATTACH로 합치는 식.

ATTACH의 무중단성

ATTACH PARTITION은 자식의 CHECK constraint가 이미 valid 상태이면 빠르게 락 잡고 끝난다 — 운영 영향 최소.

-- 패턴: ADD CONSTRAINT ... NOT VALID → VALIDATE → ATTACH
-- ATTACH 자체는 짧은 락만

PG 14+의 ALTER TABLE ... DETACH PARTITION ... CONCURRENTLY도 가능 — 자식 분리가 트래픽을 막지 않습니다.

핵심 비교

    flowchart LR
  OLD["상속 기반 (PG 9.x)<br/>+ INHERITS<br/>+ CHECK<br/>+ 트리거"]
  NEW["선언적 (PG 10+)<br/>PARTITION BY"]
  OLD -. 마이그레이션 .-> NEW

  classDef old fill:#fed7aa,stroke:#c2410c,color:#7c2d12
  classDef new fill:#d1fae5,stroke:#047857,color:#064e3b
  class OLD old
  class NEW new
  
결정
새 프로젝트선언적
기존 상속 시스템마이그레이션 검토
PG 9.x 호환 필요상속이 어쩔 수 없는 선택 (지원 끝남)
데이터 모델로서의 상속별개 문제 — 파티셔닝과 분리해 검토
상속이 살아 있는 코드를 만나면 우선 PG 메이저 버전을 확인하라. PG 10+에서 상속 기반 그대로면 옛 운영자의 결정이 굳어 있는 경우가 많습니다. 선언적으로 옮길지 검토 가치 있습니다.

정리

  • 상속 기반 파티셔닝 = PG 9.x의 패턴입니다. INHERITS + CHECK + 트리거
  • 선언적 파티셔닝 = PG 10+. 코어가 라우팅·pruning 모두 처리
  • 거의 모든 파티셔닝 목적은 선언적이 정답
  • 옛 상속 시스템은 ATTACH PARTITION으로 마이그레이션 가능
  • 상속의 데이터 모델적 활용은 파티셔닝과 별개

다음 절(7.4)에서는 파티셔닝의 핵심 효과인 partition pruning을 봅니다.