반정규화: 성능을 위한 의도적 중복

데이터 모델링의 이해 · 정규화와 반정규화

이 페이지의 목차

핵심 요약

정규화된 모델을 출발점으로 반정규화의 적용 조건·기법·무결성 비용을 판단한다.

SQLD·SQLP에서는 반정규화를 정규화 이후의 선택적 성능 조치로 판단한다. 특정 DBMS의 Materialized View나 캐시 기능은 대안 후보일 뿐 반정규화의 정의와 같지 않다.

학습 목표

  • 반정규화가 정규화의 반대 작업만을 뜻하지 않는 이유를 설명한다.
  • 테이블·컬럼·관계 수준의 반정규화 기법을 사례에 적용한다.

본문

개념 지도

도식
후보키·함수 종속성→이상 현상→정규형 분해→성능 측정→제한적 반정규화

핵심 내용

반정규화는 조회 성능이나 운영 단순화를 위해 의도적으로 중복을 허용하거나 구조를 조정하는 설계다. 먼저 정규화된 모델과 실제 SQL, 데이터량, 변경 빈도를 확인한 뒤 적용한다.

  • 테이블: 병합, 분할, 요약·집계 테이블 추가
  • 컬럼: 중복 컬럼, 파생 컬럼, 이전·현재 상태값 저장
  • 관계: 잦은 조인을 줄이기 위한 중복 관계 추가

예를 들어 주문 목록에서 고객명을 매우 자주 조회한다고 주문에 고객명을 복제하면 조인은 줄지만, 고객명 변경 시 모든 주문의 값을 맞춰야 한다. 따라서 갱신 주체, 동기화 방식, 검증 절차가 설계에 포함되어야 한다.

적용 순서

  1. 병목 SQL과 업무 요구를 측정한다.
  2. 인덱스·SQL 개선·캐시 등 다른 방법을 검토한다.
  3. 읽기 이득과 쓰기·무결성 비용을 비교한다.
  4. 적용 후 성능과 불일치를 감시한다.

중복 컬럼의 Source of Truth 설계

주문에 고객명을 복제한다고 가정한다.

예시·텍스트
CUSTOMER.customer_name  현재 고객명 원천
ORDERS.customer_name    주문 당시 이름 Snapshot 또는 현재 이름 Cache

두 컬럼의 의미를 먼저 정해야 한다. 과거 주문서에 주문 당시 이름을 남기는 것이라면 값이 달라도 오류가 아니다. 현재 고객명을 빠르게 보여 주기 위한 Cache라면 고객명 변경 시 동기화가 필요하다.

동기화 방식 비교

방식장점위험
동일 Transaction에서 함께 갱신즉시 일관성여러 Table Lock·업무 결합 증가
Trigger누락 방지숨은 부하·재귀·오류 추적 어려움
Event/비동기 갱신쓰기 응답 분리일시적 불일치·재처리 필요
Batch 재생성단순·대량 효율최신성 지연
Materialized ViewQuery Rewrite·관리 기능Refresh 비용·제약

반정규화된 값마다 원천, 갱신 주체, 허용 지연, 검증 Query, 재생성 절차를 문서화한다.

적용 전후 검증

SQL
-- 중복값 불일치 점검 예
SELECT COUNT(*)
FROM   orders o
JOIN   customer c ON c.customer_id = o.customer_id
WHERE  o.cached_customer_name <> c.customer_name
   OR (o.cached_customer_name IS NULL AND c.customer_name IS NOT NULL)
   OR (o.cached_customer_name IS NOT NULL AND c.customer_name IS NULL);

읽기 SQL의 Join·Buffers가 실제로 줄었는지와 DML 시간·Redo·Lock이 얼마나 늘었는지를 함께 측정한다. 측정 근거와 복구 경로가 없다면 정규화 구조를 유지한다.


시험 포인트

반정규화는 단순히 테이블을 합치는 작업이 아니다.

수준대표 기법주된 이득함께 드는 비용
테이블병합, 수직·수평 분할, 통계·이력·요약 테이블조인·스캔 범위 감소동기화·추가 저장
컬럼중복 컬럼, 파생 컬럼, PK 컬럼의 일반 컬럼 복제반복 조인·계산 감소갱신 경로 증가
관계중복 관계 추가긴 조인 경로 단축참조 무결성 관리

자주 사용하는 소수 컬럼과 거의 사용하지 않는 큰 컬럼이 한 넓은 테이블에 섞여 블록당 행 수가 줄어든다면, 1:1 수직 분할로 핵심 컬럼을 별도 테이블에 둘 수 있다. 행의 종류나 기간별로 읽는 범위가 명확하면 수평 분할을 검토한다.

주문목록을 조회할 때 매번 제품 테이블과 조인하는 비용이 실제 병목이라면 제품명을 주문목록에 중복할 수 있지만, 제품명 변경 시 과거 주문의 이름을 바꿀지 보존할지 업무 규칙부터 정해야 한다.

적용 전 체크

  1. 정규화를 정확히 수행한다.
  2. 데이터 용량과 증가량을 산정한다.
  3. 트랜잭션 유형·빈도·범위를 파악한다.
  4. 병목 SQL을 측정한다.
  5. 인덱스나 SQL 개선으로 해결 가능한지 본다.
  6. 읽기 이득이 쓰기·무결성 비용보다 클 때만 적용한다.

정규화 → 용량 산정 → 트랜잭션 분석 → 반정규화·PK/FK·인덱스 검토의 흐름을 기억한다.

자주 틀리는 부분

  • 반정규화는 데이터 무결성을 포기하는 작업이 아니다.
  • 정규화가 항상 느리고 반정규화가 항상 빠른 것은 아니다.
  • 컬럼 중복은 저장 공간뿐 아니라 변경 경로와 잠금 범위도 늘릴 수 있다.

복습 문제

답을 생각한 뒤 정답과 근거를 펼쳐 보세요.

01

파생 금액을 저장하기 전에 정해야 할 세 가지는?

정답과 근거

원본 Source of Truth, 계산식과 반올림 규칙, 갱신 시점·실패 복구·검산 방법을 정해야 합니다.

02

반정규화 판단보다 먼저 확보해야 할 근거는 무엇인가?

정답과 근거

실행계획과 운영 지표로 확인한 반복적 성능 병목 및 다른 튜닝 대안의 한계를 근거로 확보해야 합니다.

03

중복 저장한 파생 금액이 원본 상세행과 달라졌을 때 어떤 동기화·정합성 검사가 필요한가?

정답과 근거

원본과 파생값을 같은 트랜잭션 또는 재처리 가능한 이벤트로 동기화하고, 주기적으로 재계산한 값과 비교해 차이를 탐지·복구해야 합니다.