CS/DB

[DB] 정규화, 역정규화

leejunkim 2025. 7. 18. 01:11

WeeklyPaper: 역정규화가 필요한 상황과 적용 시 고려해야 할 사항, 그리고 역정규화를 적용할 때의 장단점을 설명해주세요.


역정규화를 알아보기 전에, 먼저 정규화를 간단하게 알아보자.

정규화 (Normalization)

데이터베이스의 데이터의 중복을 줄이고 무결성을 높이기 위한 과정이다. 테이블 간의 종속정을 제거거나 분리하며, 삽입/수정/삭제 이상 (anomaly)를 방지하고, 논리적으로 일관된 데이터 구조를 만드는 것이 목적이다. 

 

정규화 유형은 이렇게 나뉜다.

1NF 모든 속성은 원자값만 포함 중복 필드 제거, 원자성 보장
2NF 1NF + 모든 비키가 기본 키에 완전 함수적 종속 부분 종속 제거
3NF 2NF + 비키 간 이행적 종속 제거 이행 종속 제거
BCNF 3NF + 모든 결정자가 후보 키 결정자 이상 제거

 

정규화 예시는 잘 정리해둔 블로그가 많기도 하고, 나중에 정규화에 대해서 더 자세히 쓰고싶어서 여기에는 추가 안할 예정이다.

 

간단한 용어 정리:

  • 속성 (attribute) = 컬럼
    • 예) 학생번호, 강좌이름, 등
  • 함수 종속 (Functional Dependency) = "하나로 결정되는 관계"
    • X를 알면 Y를 알 수 있다"는 관계를 의미한다 => Y는 X에 '종속' 되어 있다고 말한다
    • 예) 학생번호를 알면 학생이름을 알 수 있음

 

제 1 정규형 (1NF)

모든 속성의 도메인이 더 이상 분해되지 않는 원자값으로만 구성되어있다.

 

제 2 정규형 (2NF)

  • 제 1 정규형에 속한다
  • 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속된다 => 부분 함수 종속을 제거하고 모든 속성이 기본키에 완전 함수 종속되도록 릴레이션을 분해한다
    • 기본키가 여러 개일 때, 일반 컬럼이 기본키의 일부에만 의존하면 안 된다
  • 용어 정리 
    • 완전 함수 종속 = 어떤 속성이 기본키에 대해 완전히 종속되어야 한다 -> 모든 일반 컬럼은 기본키 '전체'하고만 관련 있어야 한다
    • 부분 함수 종속 = 어떤 속성이 기본키가 아닌 다른 속성에 종속되거나 기본키가 여러 속성으로 구성되어 있을경우 (composite key) 그 중 한 속성 중 일부만 종속될 때
  • 예시: 1 테이블의 기본키(PK, primary key)는 복합키: (학생번호, 강좌이름)
    • 성적 속성은 학생번호 + 강좌이름 둘다 필요하다 (OK)
    • 문제점: 강의실 속성은 강좌이름만 알면 되는데 지금은 학생번호에도 의존하고 있다! -> 부분 함수 종속!
      • 강의실은 전체 기본키 ( 학생번호, 강좌이름 )가 아니라 기본키의 일부인 강좌이름만 의존하고 있다.
      • 만약 데이터베이스 강의실이 101호에서 201호로 바뀌면, 데이터베이스를 듣는 모든 학생의 데이터를 일일이 찾아서 강의실 번호를 전부 바꿔줘야 한다.
    • 해결책 (2정규형): 이 부분 함수 종속을 없애고 별도의 테이블로 분리한다. 이제 모든 테이블에서 일반 컬럼(예: 학생 이름)은 각각의 기본키(예: 학생 ID) 전체에만 의존하게 되었다.

제 3 정규형 (3NF)

  • 제 2정규형에 속한다
  • 모든 일반 컬럼은 오직 기본키(PK)에만 직접 의존해야 한다
  • 기본키가 아닌 모든 속성이 기본키에 이행적 함수 종속이 되지 않는다
  • 이행적 함수 종속
    • A → B , B → C 인 경우 A → C 가 성립될 때
    • A를 알면 B를 알고 그를 통해 C를 알 수 있는 경우를 의미한다
  • 예시: 1 테이블 - (학생번호, 강좌이름, 수강료)
    • <학생은 계절학기 강좌 중, 한 강좌만 신청 가능> => 이 규칙 때문에 이 테이블의 기본키(PK)는 학생번호가 된다. 학생번호만 알면 그 학생이 듣는 강좌와 수강료를 알 수 있기 때문이다.
    • 문제점: 꼬리에 꼬리를 무는 의존성 ( 이행적 함수 종속 )
      • 학생번호를 알면 → 강좌이름을 알 수 있다. (A → B)
      • 강좌이름을 알면 → 수강료를 알 수 있다. (B → C)
      • 일반 속성인 수강료(C)가 기본키인 학생번호(A)에 직접적으로 의존하는 게 아니라, 다른 일반 속성인 강좌이름(B)을 거쳐서 한 다리 건너 의존하고 있다.
      • 만약 데이터베이스 강좌의 수강료를 수정하려고 하면 일일이 다 찾아서 직접 수정해야한다.
    • 해결책 (3정규형): 1테이블의 의존의 연결고리를 끊어내고, 직접적인 관계만 남도록 테이블을 분리한다

BCNF

  • 3정규형보다 조금 더 엄격한 규칙으로, 3정규형만으로는 해결되지 않는 특수한 문제를 다룬다
  • 규칙: 모든 결정자는 후보키여야 한다
  • 용어 정리
    • 후보키 (Candidate Key): 테이블에서 각 행을 유일하게 식별할 수 있는 최소한의 컬럼 조합. 기본키(PK)가 될 수 있는 후보들이다.
    • 결정자 (Determinant): 쉽게 말해 '뭔가를 결정하는 녀석' . A → B 관계(A를 알면 B를 안다)에서 A가 바로 결정자.

예시를 보자:

  • 규칙들
    • 한 교수는 하나의 특강만 담당 (즉 교수를 알면, 특강이름이 결정됨)
    • 한 학생은 특정 특강을 1번만 들을 수 있음
    • 이 규칙들로 인해 테이블의 후보키는 (학생번호, 특강이름)임
  • 테이블의 결정자
    • (학생번호, 특강이름) -> 교수: (학생번호, 특강이름)은 교수를 결정하고, (학생번호, 특강이름)은 후보키여서 BCNF규칙에 OK임
    • 교수 -> 특강이름: 교수는 특강이름을 결정하지만, 교수는 후보키가 아니다 (BCNF 위반)!
  • 문제점: 교수 -> 특강이름 관계에서, 후보키가 아닌 교수가 결정자이다
    • 만약 401번 학생이 '소셜네트워크' 특강 수강을 취소하면 테이블에서 (401, 소셜네트워크, 김교수) 행을 삭제해야 한다.
    • 그런데 501번 학생도 수업을 취소해서 (501, 소셜네트워크, 김교수) 행까지 모두 지우게 되면, '김교수가 소셜네트워크 특강을 담당한다'는 정보까지 데이터베이스에서 함께 사라진다.
    • 이것이 바로 3정규형을 만족해도 발생하는 '이상 현상(Anomaly)'다
  • 해결책 (BCNF): 문제가 되는 결정자(교수)를 기반으로 테이블을 분리한다.
    • 오른쪽 그림처럼 (학생번호, 교수) 테이블과 (교수, 특강이름) 테이블 두 개로 나눈다.

역정규화 (De-normalization)

정규화된 데이터베이스 구조에서 성능이나 관리, 편의성을 위해 일부러 정규화를 완화하는 기법이다. 즉, 일부러 정규화 했던 것을 되돌리는 것을 말한다.

데이터 중복을 감수하고, 조인을 줄이거나 성능을 개선하기 위해 사용된다.

정규화 vs 역정규화

  정규화 역정규화
목적 데이터 무결성, 중복 최소화 성능 개선, 조인 최소화
특징 테이블 분리, 관계 명확 일부 데이터 중복 허용
주 용도 데이터 정합성 유지 우선 조회 성능 최적화, 복잡한 보고서 등
데이터 일관성 자동 보장 (제약조건) 수동 유지 필요 (트리거, 배치 등)

 

역정규화의 장단점 분석

  장점 당점
성능 JOIN 감소, 조회 속도 향상 갱신 복잡성 증가 (INSERT, UPDATE 추가 작업 필요)
유지보수 데이터 구조가 단순, 특정 쿼리에서 편리 데이터 중복으로 정합성 문제 발생 위험
활용성 통계, 보고서 등 반복 조회 시 성능 유리 스키마 변경 시 영향 범위가 넓음
트레이드오프 읽기 성능 ↑, 쓰기 복잡도 ↑ 읽기 속도 ↔ 유지보수 비용 판단 필요

 

역정규화 적용 기준

  • 테이블 조인 휫수가 많고 성능 저하가 발생하는 경우
    • 가장 일반적인 역정규화의 이유. 특정 데이터를 가져오기 위해 join을 2-3번 이상을 해야한다면 성능에 큰 부담을 줄 수 있다.
  • 자주 사용되는 복잡한 쿼리가 반복되는 이유 (대시보드, 보고서 등)
    • 매번 요청이 올때마다 복잡한 계산을 수행하는 대신, 계산된 결과를 별도의 컬럼에 저장해두는 방식이다.
  • 데이터의 실시간성보다 빠른 조회가 중요한 경우 (캐시 개념)
  • 집계나 통계가 반복적으로 필요할 경우

사례 예시

  설명 효과
1 회원 테이블에 최근 주문일자 추가 주문 조인 없이 조회 가능
2 게시판 테이블에 댓글 수 컬럼 추가 매 조회 시 COUNT() 방지
3 상품 테이블에 브랜드명 중복 저장 조인 감소, 가독성 향상
4 판매 통계 테이블에 월/일자별 합계 컬럼 추가 집계 성능 향상
5 결제 이력에 회원 나이 그룹(20대/30대 등) 캐시 빠른 분석용 집계 가능
6 주문 상세에 카테고리명, 상품명 중복 저장 보고서, 검색 속도 개선

 

주의점

  • 데이터 불일치 위험 
    • 데이터를 중복으로 저장하기 때문에, 원본 데이터가 수정될 때 중복 저장된 모든 데이터를 함께 수정해주어야 한다 => 이를 놓치면 데이터가 서로 맞지 않는 심각한 문제가 발생할 수 있다. (예: 상품 테이블의 카테고리명은 'A'인데, 실제 카테고리 테이블의 이름은 'B'로 바뀐 상황)
  • 저장 공간 증가
    • 중복 데이터만큼 더 많은 저장 공간이 필요하다.
  • 데이터 수정 비용 증가
    • 하나의 데이터를 수정하기 위해 여러 테이블이나 컬럼을 동시에 업데이트해야 하므로 INSERT, UPDATE, DELETE 작업이 더 복잡해지고 느려질 수 있다.

출처

'CS > DB' 카테고리의 다른 글

[DB] SQL - DDL, DML, DCL  (3) 2025.07.14