CodeGym /행동 /SQL SELF /정규화와 성능 사이의 균형

정규화와 성능 사이의 균형

SQL SELF
레벨 26 , 레슨 3
사용 가능

데이터를 완벽하게 정규화하면, 각 테이블이 엄청 컴팩트해지고, 그 안의 정보는 딱 한 가지 원칙만 따르게 돼. 근데 실제로 쿼리를 날릴 때(예: "어떤 학생들이 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이 더 나은 경우도 있어:

자주 쓰는 집계값

예를 들어, 시스템이 매일 각 강좌별 학생 수를 세는 쿼리를 날린다고 해보자. 정규화된 구조에서는 JOINCOUNT()를 계속 돌려야 해. 대신 "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_itemsproducts를 조인하는 과정이 데이터가 많아질수록 쿼리를 느리게 만들어.

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 데이터 업데이트는 자동화하자. 트리거나 작업 스케줄러를 써서 불일치 문제를 막자.

2
과제
SQL SELF, 레벨 26, 레슨 3
잠금
데이터 집계 속도 향상을 위한 비정규화
데이터 집계 속도 향상을 위한 비정규화
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION