정규화는 성능을 포기하고 정합성을 사는 일이 아닙니다. 대부분의 조회에서는 정규화가 성능까지 같이 벌어 줍니다.
풀고 시작
문제 1. 주식별자가 학번과 과목코드인 수강 엔터티에 학생이름이 들어 있습니다. 학생이름은 학번만으로 결정됩니다. 이 상태를 해소하는 정규화 단계는?
복합 주식별자의 일부에만 종속된 속성을 떼어 내는 것이 제2정규화입니다. 제1정규형은 다중값을 없애는 단계이고, 제3정규형은 키가 아닌 속성 사이의 종속을 없애는 단계라 상황이 다릅니다.
함수적 종속이라는 한 문장
정규화를 외우려 들면 정규형 이름 대여섯 개가 뒤섞입니다. 그런데 정규화의 재료는 딱 하나, 함수적 종속(functional dependency)입니다. 속성 A의 값이 정해지면 속성 B의 값도 하나로 정해질 때, B는 A에 함수적으로 종속된다고 말합니다. 주민등록번호가 정해지면 생년월일이 정해지죠. 반대는 성립하지 않습니다.
정규화는 이 종속 관계를 보고 "이 속성은 이 키에 딸린 것이 맞는가"를 계속 묻는 작업입니다. 딸린 곳이 아닌 자리에 앉아 있는 속성을 제 자리로 옮기는 것이 전부입니다. 그리고 제 자리로 옮기면 자연히 중복이 사라지고, 1강에서 본 삽입과 갱신과 삭제 이상 현상이 함께 사라집니다.
세 단계를 한 줄씩
제1정규형은 모든 속성이 원자값을 갖는 상태입니다. 한 칸에 값이 여러 개 들어 있거나 반복되는 그룹이 있으면 여기서 걸립니다. 3강에서 다중값 속성을 별도 엔터티로 분리한다고 했던 그 이야기입니다.
제2정규형은 제1정규형이면서 부분 함수적 종속이 없는 상태입니다. 주식별자가 여러 속성으로 이뤄졌을 때만 문제가 생깁니다. 수강 엔터티의 키가 학번과 과목코드인데 학생이름이 학번만으로 결정된다면, 학생이름은 키의 일부에만 딸려 있습니다. 이 속성을 학생 엔터티로 옮기는 것이 제2정규화입니다. 그래서 주식별자가 단일 속성이면 제2정규형 위반은 원리상 발생하지 않습니다.
제3정규형은 제2정규형이면서 이행 함수적 종속이 없는 상태입니다. 키가 아닌 속성이 다른 키가 아닌 속성을 결정하는 상황이죠. 사원 엔터티에 부서코드와 부서명이 같이 있으면, 사번이 부서코드를 결정하고 부서코드가 부서명을 결정합니다. 부서명은 사번에 직접 딸린 것이 아니라 부서코드를 거쳐 딸려 있으니 부서 엔터티로 옮겨야 합니다.
정규형
없애는 것
알아보는 신호
제1정규형
다중값과 반복 그룹
한 칸에 값이 여러 개 보인다
제2정규형
부분 함수적 종속
복합 키의 일부만으로 결정되는 속성이 있다
제3정규형
이행 함수적 종속
키가 아닌 속성이 다른 속성을 결정한다
보이스코드 정규형
후보키가 아닌 결정자
결정자인데 후보키가 아닌 속성이 있다
보이스코드 정규형은 제3정규형을 조금 더 조인 것입니다. 어떤 속성이 다른 속성을 결정한다면 그 결정자는 후보키여야 한다는 조건이죠. 후보키가 여러 개 있고 서로 겹치는 특수한 상황에서만 문제가 되므로, 시험에서는 정의만 정확히 기억해 두면 충분합니다.
정규화가 성능을 해친다는 오해
"정규화하면 조인이 늘어 느려진다"는 말을 자주 듣습니다. 절반만 맞습니다.
정규화하면 테이블 하나에 담긴 컬럼 수가 줄어들고, 그러면 같은 블록에 더 많은 행이 들어갑니다. 그 결과 특정 컬럼만 훑는 조회에서는 읽어야 할 블록 수가 줄어 오히려 빨라집니다. 게시판 목록을 뽑을 때 본문이 다른 테이블에 있으면 목록 조회가 가벼워지는 것이 이 원리죠.
느려지는 쪽은 여러 테이블의 값을 한 번에 다 봐야 하는 조회입니다. 조인이 늘고, 조인 대상이 크면 비용이 커집니다. 즉 정규화가 성능에 미치는 영향은 조회 패턴에 따라 방향이 갈립니다. 시험에서 "정규화는 항상 성능을 저하시킨다"는 보기가 나오면 그것이 틀린 보기입니다.
반정규화는 마지막에 꺼내는 카드입니다
반정규화(denormalization)는 정규화된 모델을 성능이나 개발 편의를 위해 일부러 되돌리는 일입니다. 중요한 것은 순서입니다. 정규화를 먼저 하고, 성능 문제가 실제로 확인된 자리에만 반정규화를 적용합니다. 처음부터 반정규화된 모델을 그리면 무엇을 왜 되돌렸는지 아무도 알 수 없게 됩니다.
반정규화의 방법은 대상에 따라 나뉩니다. 테이블 반정규화로는 자주 함께 조회되는 테이블을 병합하거나, 반대로 큰 테이블을 행 단위나 컬럼 단위로 분할하거나, 집계용 테이블을 새로 추가합니다. 컬럼 반정규화로는 조인을 없애기 위해 중복 컬럼을 두거나, 파생 컬럼을 미리 계산해 둡니다. 관계 반정규화로는 조인 경로를 줄이려고 중복 관계선을 추가합니다.
값을 지불한다는 사실을 잊지 않는 것이 핵심입니다. 반정규화로 얻는 것은 조회 속도이고, 잃는 것은 정합성을 지키는 비용입니다. 중복 컬럼을 두었다면 원본이 바뀔 때 그 컬럼도 반드시 함께 바꿔 주는 장치가 있어야 합니다. 그 장치를 만들지 않은 반정규화는 성능 개선이 아니라 시간을 두고 터지는 데이터 오류입니다.
다음 강에서는 이렇게 나눈 모델이 조인과 트랜잭션과 NULL에서 어떻게 드러나는지 봅니다.
인출 문제
문제 1. 제3정규형 위반에 해당하는 상황은?
제3정규형은 이행 함수적 종속을 없애는 단계입니다. 첫 번째는 제1정규형, 두 번째는 제2정규형 위반이고, 마지막은 보이스코드 정규형에서 다루는 상황입니다.
문제 2. 주식별자가 단일 속성인 엔터티에서 원리상 발생할 수 없는 정규형 위반은?
제2정규형 위반은 복합 키의 일부에만 종속되는 속성이 있을 때 생기므로 키가 하나면 성립하지 않습니다. 다중값 문제와 이행 종속 문제는 키가 하나여도 얼마든지 생깁니다.
문제 3. 정규화와 성능의 관계에 대한 설명으로 가장 적절한 것은?
정규화는 테이블을 가볍게 만들어 특정 컬럼만 읽는 조회를 빠르게 하기도 하고, 여러 테이블의 값을 함께 봐야 하는 조회를 느리게 하기도 합니다. 방향이 조회 패턴에 따라 갈리므로 항상 저하된다는 말은 틀립니다.
문제 4. 반정규화를 적용할 때의 원칙으로 옳지 않은 것은?
처음부터 반정규화된 모델은 무엇을 왜 되돌렸는지 근거를 남기지 못해 이후 유지보수가 어려워집니다. 반정규화는 정규화된 모델을 기준으로, 문제가 확인된 자리에만 값을 치르고 적용하는 예외 처리입니다.
더 풀기
출제기준의 세세항목을 따라 이 강의 범위에서 새로 낸 문제입니다. 모의고사도 여기에서 뽑습니다.
묶음 1
문제 1. 함수적 종속을 가장 잘 설명한 것은?
사원번호가 정해지면 사원 이름이 하나로 정해집니다. 이때 이름은 사원번호에 함수적으로 종속됩니다. 값이 같다는 것과는 전혀 다른 개념입니다.
문제 2. 부분 함수 종속을 가장 잘 설명한 것은?
부분 종속은 복합키가 있을 때만 생깁니다. 일반 속성에 종속되는 것은 이행 종속입니다.
문제 3. 이행 함수 종속을 가장 잘 설명한 것은?
사원번호가 부서코드를 정하고 부서코드가 부서명을 정한다면 부서명은 사원번호에 간접적으로 종속됩니다. 이런 구조를 3정규형에서 걷어냅니다.