핵심 요약
정규화된 모델을 출발점으로 반정규화의 적용 조건·기법·무결성 비용을 판단한다.
SQLD·
학습 목표
- 반정규화가 정규화의 반대 작업만을 뜻하지 않는 이유를 설명한다.
- 테이블·컬럼·관계 수준의 반정규화 기법을 사례에 적용한다.
본문
개념 지도
후보키·함수 종속성→이상 현상→정규형 분해→성능 측정→제한적 반정규화핵심 내용
반정규화는 조회 성능이나 운영 단순화를 위해 의도적으로 중복을 허용하거나 구조를 조정하는 설계다. 먼저 정규화된 모델과 실제 SQL, 데이터량, 변경 빈도를 확인한 뒤 적용한다.
- 테이블: 병합, 분할, 요약·집계 테이블 추가
- 컬럼: 중복 컬럼, 파생 컬럼, 이전·현재 상태값 저장
- 관계: 잦은 조인을 줄이기 위한 중복 관계 추가
예를 들어 주문 목록에서 고객명을 매우 자주 조회한다고 주문에 고객명을 복제하면 조인은 줄지만, 고객명 변경 시 모든 주문의 값을 맞춰야 한다. 따라서 갱신 주체, 동기화 방식, 검증 절차가 설계에 포함되어야 한다.
적용 순서
- 병목 SQL과 업무 요구를 측정한다.
- 인덱스·SQL 개선·캐시 등 다른 방법을 검토한다.
- 읽기 이득과 쓰기·무결성 비용을 비교한다.
- 적용 후 성능과 불일치를 감시한다.
중복 컬럼의 Source of Truth 설계
주문에 고객명을 복제한다고 가정한다.
CUSTOMER.customer_name 현재 고객명 원천
ORDERS.customer_name 주문 당시 이름 Snapshot 또는 현재 이름 Cache
두 컬럼의 의미를 먼저 정해야 한다. 과거 주문서에 주문 당시 이름을 남기는 것이라면 값이 달라도 오류가 아니다. 현재 고객명을 빠르게 보여 주기 위한 Cache라면 고객명 변경 시 동기화가 필요하다.
동기화 방식 비교
| 방식 | 장점 | 위험 |
|---|---|---|
| 동일 Transaction에서 함께 갱신 | 즉시 일관성 | 여러 Table Lock·업무 결합 증가 |
| Trigger | 누락 방지 | 숨은 부하·재귀·오류 추적 어려움 |
| Event/비동기 갱신 | 쓰기 응답 분리 | 일시적 불일치·재처리 필요 |
| Batch 재생성 | 단순·대량 효율 | 최신성 지연 |
| Materialized View | Query Rewrite·관리 기능 | Refresh 비용·제약 |
반정규화된 값마다 원천, 갱신 주체, 허용 지연, 검증 Query, 재생성 절차를 문서화한다.
적용 전후 검증
-- 중복값 불일치 점검 예
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·
시험 포인트
반정규화는 단순히 테이블을 합치는 작업이 아니다.
| 수준 | 대표 기법 | 주된 이득 | 함께 드는 비용 |
|---|---|---|---|
| 테이블 | 병합, 수직·수평 분할, 통계·이력·요약 테이블 | 조인·스캔 범위 감소 | 동기화·추가 저장 |
| 컬럼 | 중복 컬럼, 파생 컬럼, PK 컬럼의 일반 컬럼 복제 | 반복 조인·계산 감소 | 갱신 경로 증가 |
| 관계 | 중복 관계 추가 | 긴 조인 경로 단축 | 참조 무결성 관리 |
자주 사용하는 소수 컬럼과 거의 사용하지 않는 큰 컬럼이 한 넓은 테이블에 섞여 블록당 행 수가 줄어든다면, 1:1 수직 분할로 핵심 컬럼을 별도 테이블에 둘 수 있다. 행의 종류나 기간별로 읽는 범위가 명확하면 수평 분할을 검토한다.
주문목록을 조회할 때 매번 제품 테이블과 조인하는 비용이 실제 병목이라면 제품명을 주문목록에 중복할 수 있지만, 제품명 변경 시 과거 주문의 이름을 바꿀지 보존할지 업무 규칙부터 정해야 한다.
적용 전 체크
- 정규화를 정확히 수행한다.
- 데이터 용량과 증가량을 산정한다.
- 트랜잭션 유형·빈도·범위를 파악한다.
- 병목 SQL을 측정한다.
- 인덱스나 SQL 개선으로 해결 가능한지 본다.
- 읽기 이득이 쓰기·무결성 비용보다 클 때만 적용한다.
정규화 → 용량 산정 → 트랜잭션 분석 → 반정규화·PK/FK·인덱스 검토의 흐름을 기억한다.
자주 틀리는 부분
- 반정규화는 데이터 무결성을 포기하는 작업이 아니다.
- 정규화가 항상 느리고 반정규화가 항상 빠른 것은 아니다.
- 컬럼 중복은 저장 공간뿐 아니라 변경 경로와 잠금 범위도 늘릴 수 있다.
복습 문제
답을 생각한 뒤 정답과 근거를 펼쳐 보세요.
파생 금액을 저장하기 전에 정해야 할 세 가지는?
정답과 근거
원본 Source of Truth, 계산식과 반올림 규칙, 갱신 시점·실패 복구·검산 방법을 정해야 합니다.
반정규화 판단보다 먼저 확보해야 할 근거는 무엇인가?
정답과 근거
실행계획과 운영 지표로 확인한 반복적 성능 병목 및 다른 튜닝 대안의 한계를 근거로 확보해야 합니다.
중복 저장한 파생 금액이 원본 상세행과 달라졌을 때 어떤 동기화·정합성 검사가 필요한가?
정답과 근거
원본과 파생값을 같은 트랜잭션 또는 재처리 가능한 이벤트로 동기화하고, 주기적으로 재계산한 값과 비교해 차이를 탐지·복구해야 합니다.