DB/SQL 전문가 가이드

[과목 I 2장 1절] 정규화

꾸준함. 2025. 3. 17. 17:01

1. 제1정규형: 모든 속성은 반드시 하나의 값을 가져야 한다

  • 하나의 속성에는 하나의 값을 가져야지만 제1정규형을 만족시킴
  • 아래 모델과 같이 연락처 속성에 다중 값(multivalued)이 들어가는 경우 다음과 같은 문제점이 발생할 수 있음
    • 연락처 정보에서 집전화 번호와 핸드폰 번호를 구별하기 어려움
    • A 고객은 집전화가 여러 대고, B 고객은 핸드폰인 여러 대라면 혼재된 속성에서 원하는 속성 값을 추출하기 어려움
    • 명확하지 않은 속성은 이메일처럼 다른 유형의 데이터를 포함할 수도 있어 본연의 의미가 퇴색될 수 있음
    • 데이터를 아래와 같이 관리한다면 개발 복잡성이 증가하고, 연락처 속성에 담긴 본래의 의미가 점차 퇴색될 것

 

https://highllight.tistory.com/68

고객번호 고객명 연락처
10000 정우진 02-123-4567, 010-1234-5678
10001 한형식 010-5678-2345
10002 황영은 02-345-3456, 010-4567-7890

 

 

  • 다음과 같이 고객연락처라는 엔티티를 추가하면 다중 값에 대한 문제점을 해결할 수 있음
    • 고객의 연락처가 많아져도 문제가 발생하지 않음
    • 이처럼 다중 값을 제거함으로써 속성을 더 명확하게 활용할 수 있으며 개발 복잡성을 감소시킬 수 있음

 

https://highllight.tistory.com/68

고객번호 고객명
10000 정우진
10001 한형식
10002 황영은

 

고객번호 순번 연락처
10000 1 02-123-4567
10000 2 010-1234-5678
10001 1 010-5678-2345
10002 1 02-345-3456
10002 2 010-4567-7890

 

 

  • 제1정규형은 다중 값 말고도 다른 유형의 중복 데이터도 의미할 수 있으며 이런 경우 중복 데이터를 속성으로 분리함에 따라 해결할 수 있음
  • 아래 모델은 주문 엔터티로 주문이 발생했을 때의 정보를 관리하는데 해당 모델을 보고 우려되는 점은 다음과 같음
    • 상품을 세 개 이상 주문할 수 없음
    • 상품 1, 상품 2 모두 빠르게 조회하고 싶을 경우 속성마다 인덱스를 추가해야 됨

 

https://highllight.tistory.com/68

 

  • 다음과 같이 주문상세 엔티티를 추가할 경우 앞서 언급한 문제를 해결할 수 있음
    • 상품을 몇 개를 주문하던 아무런 제약을 받지 않음
    • 추가적인 인덱스 또한 필요 없음

 

https://highllight.tistory.com/68

 

2. 제2정규형: 엔티티의 일반속성은 주식별자 전체에 종속적이어야 한다

  • 앞서 [그림 I -2-4]의 주문상세 모델을 보면 `상품명` 속성이 주식별자가 아닌 오직 상품번호에 대해서만 반복되어 쌓이게 되는 구조인 것을 확인할 수 있음
    • 아래 표에서 `SQL 전문가 가이드`라는 데이터가 반복되는 것을 확인 가능
    • 상품번호도 중복되지만 고객이 상품을 주문함으로써 발생하는 매핑정보로서 의미를 가지고 있고 주문번호와 함께 주문상세 엔티티의 식별자 의미를 가지고 있기에 중복된 데이터라고 볼 수 없음
    • 반면, 상품명은 주문번호와 관계없이 오직 상품번호에 의해서만 결정되므로 상품명이 주식별자인 `주문번호 + 상품번호`가 아닌 오직 상품번호에 `종속적이다`라고 표현 가능 

 

주문번호 상품번호 상품명
1100001 256 SQL 전문가 가이드
1100002 257 데이터아키텍처 전문가 가이드
1100003 256 SQL 전문가 가이드
1100004 256 SQL 전문가 가이드
1100005 258 데이터 분석 전문가 가이드

https://highllight.tistory.com/68

 

  • 함수종속성 (Functional Dependency)은 데이터들이 어떤 기준값에 의해 종속되는 현상을 지칭
    • 기준값을 결정자 (Determinant)라 하고, 종속되는 값을 종속자 (Dependent)라고 함
    • 앞선 표에서 상품명은 상품번호에 종속되어 있기에 종속자이며, 상품번호는 상품명을 결정하기에 결정자

 

  • 주문상세 엔티티의 상품명은 식별자 전체가 아닌 일부에만 종속적이며 이를 부분 종속 (Partial Dependency)라고 함
  • 이는 `엔티티의 일반속성은 부식별자 전체에 종속적이어야 한다`는 제 2정규형을 위배한 것이며 다음과 같은 문제점을 가짐
    • 상품명이 변경되고 업무적으로 반영해주어야 할 경우 주문상세의 중복된 상품명을 모두 변경해야 함
    • 주문상세의 상품명을 변경한다고 해도 특정 시점에는 아직 변경되지 않은 상품명이 존재하고, 이때 들어온 트랜잭션은 일관되지 않은 데이터를 조회

 

  • 아래와 같이 상품 엔티티를 추가하면 주문상세 엔티티의 부분 종속성을 제거할 수 있음
    • 상품명 속성을 상품 엔티티에서 관리하고 상품번호를 매핑키로 활용하여, 상품명을 확인하는 구조로 데이터를 일원화해 관리함으로써 앞서 제시한 문제점을 해결할 수 있음
    • 이로써 `일반속성은 주식별자 전체에 종속해야 한다`는 제2정규형을 만족하게 됨
    • 이렇게 데이터를 관리하면 주문상세 엔티티에서는 상품번호만 들고 있고, 상품번호를 매핑키로 상품 엔티티에서 원하는 상품정보 데이터를 가져올 수 있음 (JOIN)

 

https://highllight.tistory.com/68

상품번호 상품명
256 SQL 전문가 가이드
257 데이터아키텍처 전문가 가이드
258 데이터 분석 전문가 가이드
주문번호 상품번호
1100001 256
1100002 257
1100003 256
1100004 256
1100005 258

 

3. 제3정규형: 엔티티의 일반속성 간에는 서로 종속적이지 않는다

  • [그림 I-2-6]에서 고객번호는 주문번호에 종속적이고, 고객명은 고객번호에 종속적
    • 이는 `고객명이 주문번호에 종속적`임을 의미
    • 이것을 이행적 종속 (Transitive Dependency)이라 하고, 이행적 종속을 배제하는 것을 제3정규형이라고 함

 

주문번호 -> 고객번호 -> 고객명 => 주문번호 -> 고객명

 

  • 고객번호와 고객명 모두 주문번호에 종속하며 제2정규형은 만족하였으나, 고객명이 식별자가 아닌 일반속성에 종속적인 제3정규형 위배에 해당
    • 만약 고객이 개명하여 고객명이 변경되었다면, 주문 엔티티에 고객명을 전부 갱신해야 하는데 이는 주문과는 전혀 연관 없는 트랜잭션
    • 데이터 중복으로 인해 발생하는 문제는 성능 부하 및 정합성 오류로 제2정규형과 동일

 

  • 아래와 같이 고객 엔티티를 분리하여 관리하면 위 문제를 해결할 수 있음
    • 아래 모델의 고객 엔티티를 보면 고객 속성 변경이 주문 엔티티에 영향을 주지 않는 구조이며 데이터 중복에 대한 문제도 개선되었다고 볼 수 있음
    • 비회원도 주문이 가능한 서비스이므로 고객번호가 Nullable

 

https://highllight.tistory.com/68

 

정리

  • 정규화는 필수적이지만 무조건적이지는 않음
    • 상황에 따라서는 반정규화를 진행할 수도 있음

 

  • 하지만 중요한 것은 기본적으로 정규화를 진행한 뒤 반정규화를 고려해야 한다는 점
    • 기본적으로 정규화를 진행해야지 무분별한 반정규화를 방지하고 무심코 놓칠 수 있는 부분도 챙길 수 있음

 

4. 반정규화와 성능

  • 반정규화는 정규화를 반대로 하는 것으로 역정규화라고도 부름
    • 정규화는 데이터의 중복을 최소화했다면, 반정규화는 성능을 위해 데이터 중복을 허용하는 것
    • 주로 성능이 문제 될 때 반정규화에 대한 논의가 진행됨
    • 반정규화를 진행하면 조회 성능을 향상할 수 있겠지만 입력, 수정 및 삭제 성능은 저하될 수 있으므로 심사숙고해서 진행해야 됨

 

가. 반정규화를 적용한 모델에서 성능이 향상될 수 있는 경우

  • 아래 모델에서 고객의 편의를 위해 최근 신용카드 정보를 미리 세팅하는 요건일 경우 다음과 같이 SQL을 작성하는데 다음과 같은 성능 문제가 존재함
    • 최종결과는 1건을 가져오지만, 주문내역이 많을수록 해당 주문테이블과 결제테이블의 조인 건수가 증가하게 되며, 조인된 결제정보를 모두 읽고 내림차순 정렬하여 최근 1건의 데이터를 가져오기 때문에 1234 고객의 주문 내역이 많아질수록 성능이 나빠짐
    • 즉, 주문내역이 많을수록 조인에 대한 부하가 증가하여 성능이 나빠지는 구조

 

https://highllight.tistory.com/68


 

  • 결제 엔티티에 고객번호 속성을 반정규화함으로써 조인에 대한 성능 부하를 개선할 수 있음
    • 결제 테이블에 `고객번호 + 결제수단구분코드 + 결제일시`로 인덱스를 생성하고 `Index Range Scan Descending`으로 최종 1건의 데이터만 읽어 결제수단번호를 가져올 수 있음
    • 최적의 SQL로 성능 부하를 극적으로 개선할 수 있음

 

https://highllight.tistory.com/68


 

나. 반정규화를 적용한 모델에서 성능이 저하될 수 있는 경우

  • 아래 모델에서 주문정보는 주문 엔티티에서 가져올 수 있고, 송장번호는 배송 엔티티에서 가져올 수 있기 때문에 주문과 배송 엔티티를 함께 조인해야 함

 

https://highllight.tistory.com/68

 

  • 앞서 반정규화를 통해 성능을 향상했으므로, 이번에도 주문 엔티티에 송장번호를 반정규화하여 성능 개선을 도모했다고 가정
    • 더 이상 배송 엔티티와 조인을 하지 않아도 되기 때문에 조회 관점에서는 더 빠른 성능을 확보할 수 있었음

 

https://highllight.tistory.com/68

 

  • 위와 같이 반정규화를 진행함에 따라 주문 모델의 송장번호는 주문 시점에는 NULL 데이터가 들어가며, 배송 준비가 완료되어야 송장번호를 UPDATE 할 수 있음
    • 조회 성능 향상을 위해 반정규화를 하기 전에는 없었던 UPDATE 로직이 새로 추가
    • 정규화한 모델 (그림 I-2-11)에 적절한 인덱스가 구성되었다고 가정했을 경우 반정규화한 모델 (그림 I-2-12)이 가지는 이점은 사실 굉장히 미미할 것

 

정리

  • 반정규화는 데이터 불일치로 인한 정합성 문제뿐 아니라, 불필요한 트랜잭션으로 인한 성능 문제를 생성하기 때문에 반정규화는 꼭 필요할 때만 적용해야 함
  • 조회 성능에서 미미한 이점을 취하고, 불필요한 UPDATE로 인해 또 다른 성능을 손해 본다면 이는 합리적인 판단이라고 할 수 없음
  • 정리하면 반정규화는 `그럼에도` 진행해야 할 만한 근거가 뒷받침될 때 비로소 진행해야 함

 

참고

SQL 전문가 가이드 2020 개정판 - 한국데이터산업진흥원

반응형