1. 데이터 정규화 필수
TIP — 핵심 원칙
관계형 데이터베이스의 본질은 “쪼개고 저장하고 합쳐서 출력한다”
- MySQL은 RDBMS — 정규화가 성능·정합성·무결성을 지키는 기본 전략
- 하나의 Row를 읽는 데 블록이 많으면 I/O가 늘어난다
- JSON, HTML 데이터를 하나의 컬럼에 밀어 넣는 구조는 안티패턴 (6.5 JSON 안티패턴 참고)
TIP — 정규화 단계 요약 (1NF ~ 3NF)
실무에서는 3NF 까지만 지켜도 대부분의 이상 현상(anomaly)을 막을 수 있다
| 단계 | 규칙 | 위반 시 생기는 문제 |
|---|---|---|
| 1NF | 각 컬럼은 원자값(atomic value)만 가짐 | 컬럼에 값을 쉼표로 여러 개 저장 → 검색·집계·인덱싱 불가 |
| 2NF | 1NF + 복합 PK의 일부에만 종속되는 컬럼 분리 | 같은 정보가 여러 행에 중복 → 수정 시 일부만 고쳐 불일치(Update Anomaly) |
| 3NF | 2NF + PK가 아닌 컬럼에 종속되는 컬럼(이행 종속, transitive dependency) 분리 | 행을 지우면 관련 없는 정보까지 같이 사라짐(Delete Anomaly) 등 이상 현상 |
1NF~3NF 규칙과 이상 현상 분류는 관계 이론의 일반론이고, MySQL 매뉴얼이 규범으로 정해 둔 것은 아니다. 이 문서는 그 일반론을 MySQL 스키마 설계의 판단 기준으로 쓴다. 반대로 정규화를 끝낸 뒤 집계 쿼리에서 실제로 부딪히는 규범은 공식 기본값 쪽에 있다 — MySQL 8.4의 기본 sql_mode에는 ONLY_FULL_GROUP_BY가 들어 있어서, GROUP BY에 없고 함수적으로 종속되지도 않은 컬럼을 select 리스트에 올리면 쿼리가 거부된다. 정규화된 테이블을 합쳐 보여 주는 집계 쿼리는 처음부터 이 모드를 전제로 작성한다.
WARNING — 반정규화(Denormalization)는 언제 정당한가
정규화가 기본값이지만, 조회가 압도적으로 많고 JOIN 비용이 명확한 병목으로 확인된 경우에는 예외적으로 반정규화를 검토할 수 있음.
- 예:
orders.total_price처럼 계산 결과를 미리 컬럼에 저장해두는 캐시성 반정규화- 조건: 반정규화한 값과 원본 데이터의 동기화 책임을 명시적으로 정의해야 함 (Trigger는 7장에서 비권장 → 애플리케이션 레벨 동기화나 배치로 처리). 동기화 책임을 정하지 않으면 정합성이 조용히 깨진다
- 트리거로 동기화하려는 계획에는 공식 제약이 하나 더 걸린다 — 외래 키 액션은 트리거를 발동시키지 않는다.
ON DELETE CASCADE로 지워진 자식 행에는 동기화 트리거가 실행되지 않는다 (7.3 참고)
Advertisement