데이터를 완벽하게 정규화하면, 각 테이블이 엄청 컴팩트해지고, 그 안의 정보는 딱 한 가지 원칙만 따르게 돼. 근데 실제로 쿼리를 날릴 때(예: "어떤 학생들이 SQL 강좌에 등록했어?")는 여러 테이블을 조인해야 할 수도 있어. 테이블이 많아질수록 쿼리는 복잡해지고, DBMS는 삽질을 더 많이 하게 되지.
아마 JOIN은 이미 지난 강의에서 봤을 거야. 아래는 정규화 잘 된 데이터베이스에서 쓸 수 있는 쿼리 예시야:
SELECT students.name, courses.title
FROM students
JOIN enrollments ON students.id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.id
WHERE courses.title = 'SQL';
보기엔 쉬워 보여도, 실제로는 서버가 테이블마다 읽고, 데이터 합치고, 필터링하고... 엄청 고생해. 만약 테이블이 진짜 크면? 당연히 성능이 떨어질 수밖에 없어.
전쟁: 정규화 vs 속도
다행인지 불행인지, 현실의 데이터베이스는 타협이야. 완전한 정규화는 데이터 무결성을 보장하지만, 복잡한 쿼리 속도는 느려져. 만약 DB가 분석이나 리포트용으로 많이 쓰인다면, 가끔은 denormalization이 더 이득일 때도 있어. 이건 마치 작은 상자 10개 대신 큰 상자 하나 쓰는 거랑 비슷해: 데이터 꺼내기는 빠르지만, 다시 정리하려면 더 힘들지.
언제 정규화에 집착 안 해도 돼?
denormalization이 더 나은 경우도 있어:
자주 쓰는 집계값
예를 들어, 시스템이 매일 각 강좌별 학생 수를 세는 쿼리를 날린다고 해보자. 정규화된 구조에서는JOIN과
COUNT()를 계속 돌려야 해. 대신 "Courses" 테이블에
student_count 컬럼을 추가해서, 학생이 추가/삭제될 때마다 자동으로 업데이트하게 만들 수도 있어.
-- denormalized 컬럼
UPDATE courses
SET student_count = (
SELECT COUNT(*)
FROM enrollments
WHERE enrollments.course_id = courses.id
);
자주 하는 리포트
클라이언트가 매일 "누가, 어디서, 언제 샀어?" 같은 리포트를 원한다면, "고객이름, 상품, 날짜" 식으로 미리 만들어진 denormalized 테이블을 저장하는 게 더 편해. 메인 테이블은 커지지만, 데이터 뽑는 속도는 빨라져.
읽기는 많고, 쓰기는 적을 때
DB가 주로 읽기(예: 분석)에 쓰인다면, 정규화를 포기하고 속도를 택하는 게 나을 수도 있어.
복잡한 관계에서 조인 최소화
테이블 사이에 다단계(nested) 관계가 많고,JOIN이 악몽이 되면, 정규화 레벨을 좀 낮추는 것도 방법이야.
예시: denormalization이 어떻게 속도를 올려줄까?
인터넷 쇼핑몰의 정규화된 테이블이 있다고 해보자:
products 테이블 |
orders 테이블 |
order_items 테이블 |
|---|---|---|
| id | id | id |
| name | date | order_id |
| price | customer_id | product_id |
| quantity |
각 주문(orders)은 주문 항목(order_items)으로 구성돼. 쇼핑몰이 번 돈을 계산해보자:
SELECT SUM(order_items.quantity * products.price) AS total_revenue
FROM order_items
JOIN products ON order_items.product_id = products.id;
order_items와 products를 조인하는 과정이 데이터가 많아질수록 쿼리를 느리게 만들어.
denormalized 구조
이번엔 order_items 테이블에 "추가" 컬럼 total_price가 있다고 해보자(denormalization):
order_items 테이블 |
|---|
| id |
| order_id |
| product_id |
| quantity |
| total_price |
이제 쿼리는 엄청 간단해져:
SELECT SUM(total_price) AS total_revenue
FROM order_items;
JOIN을 안 해도 되니까, 훨씬 빨라지는 거지.
실습: "판매" DB 최적화
주어진 것: 정규화된 테이블
products 테이블 |
sales 테이블 |
|---|---|
| id | id |
| name | product_id |
| price | date |
| quantity |
문제: "각 상품별로 총 얼마 벌었어?" 같은 쿼리를 빠르게 하고 싶어.
1단계: sales 테이블에 total_price 컬럼을 추가하자:
ALTER TABLE sales ADD COLUMN total_price NUMERIC;
2단계: 기존 데이터에 이 컬럼을 채워넣자:
UPDATE sales
SET total_price = quantity * (
SELECT price
FROM products
WHERE products.id = sales.product_id
);
3단계: 이제 쿼리가 훨씬 빨라져:
SELECT product_id, SUM(total_price) AS total_revenue
FROM sales
GROUP BY product_id;
근데! denormalization에는 대가가 있어
알다시피, "빠르다"가 항상 "좋다"는 아니야. denormalization을 하면 이런 문제가 생길 수 있어:
중복 저장소
total_price 컬럼은 데이터 복사본이라서, 추가 저장 공간이 필요해.
업데이트 복잡성
만약 products 테이블에서 상품 가격이 바뀌면, total_price 컬럼도 직접 업데이트해야 해. 이러다 보면 데이터가 안 맞을 수도 있어.
삽입, 수정, 삭제 시 이상현상
denormalized 데이터를 업데이트하는 걸 까먹으면 정보가 쉽게 "불일치"될 수 있어. 예를 들어, 상품 가격이 바뀌어도 자동으로 반영되지 않아.
균형: 어떻게 골든 미들 찾을까?
뭐가 더 중요한지 선택해: 성능? 구조? DB가 읽기 위주라면 쿼리에 맞춰 구조를 바꿔도 돼.
denormalization은 꼭 필요한 부분에만. 예를 들어, 핵심 숫자나 리포트에만 적용해.
denormalized 데이터 업데이트는 자동화하자. 트리거나 작업 스케줄러를 써서 불일치 문제를 막자.
GO TO FULL VERSION