데이터 베이스 최적화를 위한 핵심 개념, 정규화와 비정규화
데이터 베이스 정규화.
데이터 베이스를 다뤄봤거나, 정보 처리 기사와 같은 IT관련 시험을 준비해 본 사람이라면 한 번쯤은 들었던 단어일 겁니다.
오늘은 이 정규화에 대한 글을 작성하고자 합니다.
1. 정규화(Normalization)란?
정규화는 데이터의 중복을 최소화하고, 무결성을 유지하며, 데이터 이상현상 방지를 위해 데이터베이스를 구조화하는 과정을 의미합니다.
즉, 데이터를 체계적으로 분류하고 테이블을 적절히 나누어 효율적인 데이터 저장과 관리가 가능하도록 설계하는 방법입니다.
정규화의 목적은 크게 네 가지로 꼽을 수 있습니다.
- 중복 데이터 제거: 불필요한 데이터 중복을 없애 저장 공간을 절약하고 관리 효율성을 높임
- 데이터 무결성 유지: 데이터 변경 시 일관성을 유지하고 오류를 방지
- 이상현상 방지: 삽입 이상, 수정 이상, 삭제 이상과 같은 문제를 해결
- 데이터베이스 성능 최적화: 데이터 검색, 수정, 삭제 등의 연산을 더 효율적으로 수행할 수 있도록 함
이 중 정규화를 통해 해결할 수 있는 문제 중 하나가 바로 이상현상 방지입니다.
이상현상 (Anomaly)
이상현상의 종류에는 크게 세 가지가 존재합니다.
삽입 이상 (Insertion Anomaly): 데이터를 추가할 때 불필요한 데이터도 함께 입력해야 하는 문제
수정 이상 (Update Anomaly): 데이터를 수정할 때 여러 곳을 수정해야 하는 문제
삭제 이상 (Deletion Anomaly): 데이터를 삭제하면 관련 없는 데이터까지 함께 삭제되는 문제
위 세 가지를 예시를 들어 한 번 살펴보도록 하겠습니다.
주문 ID | 고객명 | 전화번호 | 상품명 | 가격 | 수량 |
001 | 김고양 | 010-1234-5678 | 키보드 | 50,000 | 1 |
002 | 김멍멍 | 010-9876-5432 | 마우스 | 30,000 | 2 |
003 | 김고양 | 010-1234-5678 | 모니터 | 200,000 | 1 |
위의 테이블처럼 고객 정보와 제품 정보, 구매 정보가 하나의 테이블에 다 몰려있는 경우를 생각해 봅시다.
현재 위 테이블에서는
1. 만약 김고양의 전화번호가 변경되었다면?
- 김고양의 고객 정보가 여러 번 등장
- 김고양이 번호를 바꿀 경우 모든 행을 수정해야 함. 그러지 않을 경우 일부 행 데이터가 오래된 값으로 남을 수 있음 (수정 이상)
2. 신규 고객이 등록되었지만 아직 주문을 하지 않았다면?
- 고객명과 전화번호를 제외한 나머지 테이블 값이 없으므로 고객 정보를 저장할 수 없다! (삽입이상)
3. 김멍멍이 주문을 취소한다면?
- 김멍멍에 대한 고객 정보도 함께 사라짐 (삭제이상)
와 같은 다양한 문제가 발생하게 됩니다.
하지만 정규화를 통해 테이블을 분류하고 적절하게 나누어 (eg. 고객 테이블, 주문 테이블, 상품 테이블)
데이터를 저장한다면, 위와 같은 문제를 해결할 수 있게 됩니다.
물론 무턱대고 테이블을 분류해서는 오히려 더 큰 문제가 발생할 수 있겠죠.
이를 위해 정규화에는 아래와 같은 세 가지 원칙이 존재합니다.
정규화의 원칙 세가지
1. 정보의 무손실 : 분해된 릴레이션이 표현하는 정보는 분해되기 전의 정보를 모두 포함해야 한다.
2. 최소 데이터 중복 : 이상 현상을 제거, 데이터 중복을 최소화
3. 분리의 원칙 : 하나의 독립된 관계성은 하나의 독립된 릴레이션으로 분리해서 표현
위와 같은 원칙을 지켜 정규화를 진행한다면 이상 현상 해결, DB 확장 용이, 개념들 간의 관계 정립 등의 장점이 있지만..
릴레이션들이 잘게 분해되면서 연산이 많아져, 응답 시간이 오히려 느려질 수도 있다는 단점도 존재합니다.
이런 단점을 해결하기 위해 사용하는 방법이 바로 반정규화입니다.
2. 반정규화(Denormalization)란?
반정규화는 시스템 성능 향상을 위해 정규화된 데이터 모델을 통합하는 작업입니다.
즉, 단순하게 말해서 이름 그대로 데이터 베이스의 성능 개선을 위해 일부러 정규화 원칙을 위배하는 행위라고 생각하면 됩니다.
따라서 정규화와 반정규화는 Trade-off 관계입니다.
반정규화의 기법에는 테이블 통합이나 추가, 중복 속성 추가 등이 있습니다.
다만 정규화에서 문제라고 했던 점들을 그대로 돌려놓는 것과 마찬가지다 보니
반정규화를 수행하면 테이블이 단순해지고 관리의 효율성이 증가한다는 장점이 있지만
데이터의 일관성이나 무결성이 보장되지 않는 위험성이 증가합니다.
의도적으로 중복 데이터를 생성하다 보니 검색과 같은 조회기능은 향상되어도 갱신이나 삭제 등의 성능은 낮아질 수 있습니다.
따라서 반정규화의 대상이 되는 경우는
1. 수행 속도가 느리거나
2. JOIN 연산이 과도하게 사용되어 데이터 조회가 기술적으로 어려운 경우
3. 테이블에 많은 데이터가 존재하며, 다량의 범위나 특정 범위를 자주 처리해야 하는 경우
와 같은 경우에 사용됩니다.
3. 정규화 단계
정규화와 반정규화에 대한 정의와 장단점을 알아보았으니, 정규화의 단계에 대해 더 알아보도록 하겠습니다.
정규화의 단계는 사진과 같이 다양한 단계가 존재합니다.
주문 ID | 고객 ID | 고객명 | 전화번호 | 상품명 | 공급업체 |
001 | C001 | 김고양 | 010-1234-5678 | 키보드, 마우스 | 삼성 |
002 | C002 | 김멍멍 | 010-9876-5432 | 모니터 | LG |
003 | C003 | 김토끼 | 010-0000-0000 | 키보드, 마우스, 모니터 | 삼성, LG |
위와 같은 표가 있습니다.
이 표를 기준으로 하나씩 정규화의 과정을 거치며 정규화의 단계를 알아보도록 하겠습니다.
3.1 제 1 정규화 (INF)
모든 컬럼이 '원자값'만을 가지도록 데이터를 정리하는 과정
1NF는 다음 조건을 만족해야 합니다.
- 각 컬럼이 하나의 속성만을 가져야 한다.
- 하나의 컬럼은 같은 종류나 타입(type)의 값을 가져야 한다.
- 각 컬럼이 유일한(unique) 이름을 가져야 한다.
- 칼럼의 순서가 상관없어야 한다.
1NF 적용 후
주문 ID | 고객 ID | 고객명 | 전화번호 | 상품명 | 공급업체 |
001 | C001 | 김고양 | 010-1234-5678 | 키보드 | 삼성 |
001 | C001 | 김고양 | 010-1234-5678 | 마우스 | 삼성 |
002 | C002 | 김멍멍 | 010-9876-5432 | 모니터 | LG |
003 | C003 | 김토끼 | 010-0000-0000 | 키보드 | 삼성 |
003 | C003 | 김토끼 | 010-0000-0000 | 마우스 | 삼성 |
003 | C003 | 김토끼 | 010-0000-0000 | 모니터 | LG |
위 표와 같이 1NF를 적용한 후에는 각각의 행으로 분리하여 원자성을 유지하는 것을 확인할 수 있습니다.
3.2 제 2 정규화 (2NF)
1NF를 만족하면서, 기본키의 '일부'에 종속된 속성을 제거하는 과정
2NF는 다음을 만족해야 합니다.
- 제 1정규형을 만족해야 한다.
- 모든 컬럼이 부분적 종속이 없어야 한다. 즉, 모든 컬럼이 완전 함수 종속을 만족해야 한다.
💡 여기서 함수 종속이란 무엇일까?
부분 함수 종속 (Partial Functional Dependency)
: 기본키 중에 특정 컬럼에만 종속되는 것
완전 함수 종속 (Full Functional Dependency)
: 기본키의 부분집합이 결정자가 되어선 안 된다
2NF 적용 후
여기서 만약 (주문 ID, 고객 ID)의 복합키가 기본키라고 가정해 봅시다.
여기서 고객 정보(고객명, 전화번호)는 고객 ID만 알면 알 수 있는, 즉 (주문 ID, 고객 ID)에 종속되어 있지 않고 (고객 ID)에만 종속되는 부분적 종속을 이루고 있습니다.
이를 해결하기 위해 고객 테이블을 분리해 줍니다.
[ 고객 테이블 ]
고객 ID | 고객명 | 전화번호 |
C001 | 김고양 | 010-1234-5678 |
C002 | 김멍멍 | 010-9876-5432 |
C003 | 김토끼 | 010-0000-0000 |
[ 주문 테이블 ]
주문 ID | 고객 ID | 상품 ID | 상품명 | 공급업체 |
001 | C001 | P001 | 키보드 | 삼성 |
001 | C001 | P002 | 마우스 | 삼성 |
002 | C002 | P003 | 모니터 | LG |
003 | C003 | P001 | 키보드 | 삼성 |
003 | C003 | P002 | 마우스 | 삼성 |
003 | C003 | P003 | 모니터 | LG |
2NF를 적용한 후에는 고객 정보를 분리하여 부분 함수 종속이 제거된 것을 확인할 수 있습니다.
3.3 제 3 정규화 (3NF)
2NF를 만족하면서, 기본키에 '직접' 종속되지 않는 속성을 제거하는 과정
3NF는 다음을 만족해야 합니다.
- 제 2정규형을 만족해야 한다.
- 기본키를 제외한 속성들 간의 이행 종속성 (Transitive Dependency)이 없어야 한다.
💡 이행 함수 종속 (Transitive Functional Dependency) 이란?
: A → B, B → C 일 때 A → C 를 만족하는 것
3NF 적용 후
현재 상품 ID -> 상품명, 상품명 -> 공급업체 일 때 상품 ID -> 공급업체를 만족하고 있습니다.
즉, 이행 함수 종속이 존재하므로 상품 테이블을 추가합니다.
[ 고객 테이블 ]
고객 ID | 고객명 | 고객전화번호 |
C001 | 김고양 | 010-1234-5678 |
C002 | 김멍멍 | 010-9876-5432 |
C003 | 김토끼 | 010-0000-0000 |
[ 상품 테이블 ]
상품 ID | 상품명 | 공급업체 |
P001 | 키보드 | 삼성 |
P002 | 마우스 | 삼성 |
P003 | 모니터 | LG |
[ 주문 테이블 ]
주문 ID | 고객 ID | 상품 ID |
001 | C001 | P001 |
001 | C001 | P002 |
002 | C002 | P003 |
003 | C003 | P002 |
003 | C003 | P001 |
003 | C003 | P003 |
3NF를 적용한 후에는 상품명 → 공급업체 관계를 제거하여 이행적 함수 종속 제거를 확인할 수 있습니다.
3.4 보이스-코드 정규형 (BCNF)
3NF를 만족하면서, 후보키가 아닌 속성이 후보키를 결정하지 않도록 정리하는 과정
BCNF는 다음을 만족해야 합니다.
- 제 3정규형을 만족해야 한다.
- 모든 결정자가 후보키 집합에 속해야 한다.
BCNF 적용 후
현재 테이블에서는 상품 ID가 공급업체를 결정하는 관계가 있습니다.
하지만 공급업체가 특정 상품을 공급하는 것이 아니라, 상품을 공급하는 업체가 여러 개일 수도 있는 구조라면 BCNF를 만족하지 않습니다.
[ 공급업체 테이블 ] 추가
공급업체 ID | 공급업체명 |
S001 | 삼성 |
S002 | LG |
[ 상품 테이블 ] 업데이트됨
상품 ID | 상품명 |
P001 | 키보드 |
P002 | 마우스 |
P003 | 모니터 |
[ 상품 - 공급업체 테이블 ]
상품 ID | 공급업체 ID |
P001 | S001 |
P002 | S001 |
P003 | S002 |
BCNF를 적용한 후에는 공급업체 테이블을 분리한 것을 확인할 수 있습니다.
3.5 제 4 정규화 (4NF)
BCNF를 만족하면서, 다치 종속을 제거하는 과정
4NF는 다음을 만족해야 합니다.
- BCNF를 만족해야 한다.
- 다중값 종속(다치 종속)이 없어야 한다.
💡 다치 종속 (Multi-valued Dependency) 이란?
같은 테이블 내의 독립적인 두 개 이상의 컬럼이 또 다른 컬럼에 종속되는 것
즉, A → B 인 의존성에서 단일 값 A와 다중 값 B가 존재한다면 다치 종속이라고 할 수 있습니다. 이러한 종속을 A ↠ B (이중 화살표 ↠) 로 표기합니다. 다치 종속은 최소 2개의 컬럼이 다른 컬럼에 종속되어야 하기 때문에 최소 3개의 컬럼이 필요합니다.
4NF 적용 후
위의 고객테이블에 주소라는 컬럼을 하나 추가한다고 가정해 봅시다.
고객이 여러 개의 전화번호와 주소를 가질 수 있는 경우,
현재 테이블에서는 고객 ID → 전화번호, 고객ID → 주소라는 다치 종속 관계가 발생합니다.
[ 고객 테이블 ]
고객 ID | 고객명 |
C001 | 김고양 |
C002 | 김멍멍 |
C003 | 김토끼 |
[ 고객 전화번호 테이블 ]
고객 ID | 전화번호 |
C001 | 010-1234-5678 |
C001 | 070-0000 |
C002 | 010-9876-5432 |
C002 | 010-0000-0000 |
[ 고객 주소 테이블 ]
고객 ID | 주소 |
C001 | 서울특별시 |
C001 | 부산광역시 |
C002 | 경기도 |
C002 | 대전광역시 |
다치 종속을 제거하여 각 고객이 여러 개의 전화번호와 주소를 가질 수 있도록 변경된 것을 확인할 수 있습니다.
물론 예시가 전화번호가 보니 좀 어색하게 느껴지실 수 있지만... 제 머릿속 예시의 최선이라 생각하고... 양해를...🥲
3.6 제 5 정규화 (5NF)
4NF를 만족하면서, 조인 종속을 제거하는 과정
5NF는 다음을 만족해야 합니다.
- 4NF를 만족해야 한다.
- 더 이상 비손실 분해를 할 수 없어야 한다.
💡 조인 종속 (Joint dependency) 이란?
하나의 릴레이션을 여러 개의 릴레이션으로 분해하였다가 다시 조인했을 때 데이터 손실이 없고 필요 없는 데이터가 생기는 것
조인 종속성은 다치 종속의 개념을 더 일반화한 것입니다.
5NF 적용 전
현재 고객 ID → 상품 ID, 상품 ID → 공급업체 ID 관계가 있지만 고객 ID → 공급업체 ID 관계는 명확하지 않습니다.
고객이 모든 공급업체를 통해 상품을 구매하는 것이 아니므로 조인을 하면 의미 없는 데이터, 즉 조인 종속이 생성될 위험이 있습니다.
주문 ID | 상품 ID | 공급업체 ID |
001 | P001 | S001 |
001 | P002 | S001 |
002 | P003 | S002 |
003 | P002 | S001 |
003 | P001 | S001 |
003 | P003 | S002 |
5NF 적용 후
[ 고객 - 상품 테이블 ]
고객 ID | 상품 ID |
C001 | P001 |
C001 | P002 |
C002 | P001 |
C002 | P002 |
C003 | P003 |
[ 상품 - 공급업체 테이블 ]
상품 ID | 공급업체 ID |
P001 | S001 |
P002 | S002 |
P003 | S003 |
5NF에서는 조인 종속성을 제거하여 더 이상 분해할 필요 없는 구조로 정리합니다.
고객과 공급업체 간의 직접적인 관계를 없애고, 상품을 중심으로 관계를 정리하여 데이터 무결성을 유지하여 데이터를 다시 조인하더라도 불필요한 조합이 생성되지 않습니다.
이런 과정을 통해 데이터베이스의 정규화가 진행됩니다.
물론 정규화를 진행해도 5NF 정규화까지 진행되지 않는 경우도 있습니다. 너무 과도한 정규화는 성능 저하의 원인이 될 수 있으니 규모에 알맞게 잘 적용하도록 합시다💪