반정규화로 설계하는 데이터베이스 조회 성능

반정규화의 유형과 데이터 정합성 관리 방법을 정리하고, 조인·디스크 I/O 병목을 줄이기 위한 데이터베이스 설계 판단 기준을 다룬다.

2026-08-15 · 최초 발행 2025-08-10

조회 병목이 설계를 다시 보게 만드는 시점

정규화는 중복을 줄이고 데이터 무결성을 지키는 데 적합하다. 다만 테이블 관계가 복잡해지면 조인이 늘고, 여러 테이블을 읽는 과정에서 쿼리 성능과 디스크 I/O가 부담이 될 수 있다. 실시간 응답이 필요한 시스템에서는 이 비용이 설계 선택의 기준이 된다.

반정규화(De-Normalization)는 성능 개선과 시스템 단순화를 위해 정규화된 모델에 의도적인 중복을 도입하는 과정이다. 정규화의 대체재라기보다, 정규화를 기반으로 조회 경로를 조정하는 최적화 전략에 가깝다.

이 선택은 항상 다음의 균형을 전제로 한다.

  • 데이터 정합성과 무결성, 성능 향상
  • 저장 공간 효율성, 조회 속도 개선
  • 갱신 비용 증가, 조회 비용 감소

테이블 구조에서 조인과 데이터량을 조정하는 방법

관련 테이블을 합쳐 조인을 없애거나, 반대로 큰 테이블을 접근 방식에 맞춰 나눌 수 있다.

테이블 병합

함께 조회되는 테이블을 통합하면 조인 연산을 줄일 수 있다. 고객과 주문 정보를 하나의 테이블로 다루는 경우가 그 예다.

정규화: 고객 테이블1:N 관계정규화: 주문 테이블반정규화: 통합 고객주문테이블

테이블 분할

대용량 테이블은 관리와 조회 특성에 따라 분할할 수 있다. 수직 분할은 자주 쓰는 컬럼과 드물게 쓰는 컬럼을 분리하는 방식이다.

원본 테이블자주 사용하는 컬럼 테이블드물게 사용하는 컬럼 테이블

수평 분할은 연도별·지역별처럼 특정 기준에 따라 행을 나누는 방식이다.

전체 주문 테이블2022년 주문2023년 주문2024년 주문

조회 목적의 별도 테이블

집계 결과, 변경 이력, 자주 접근하는 데이터는 별도 테이블로 관리할 수 있다. 통계 테이블에는 미리 계산한 집계 데이터를 저장하고, 이력 테이블에는 변경 내역을 남기며, 부분 테이블에는 빈번히 접근하는 데이터만 둔다.

일별 판매 통계 테이블의 예시는 다음과 같다.

CREATE TABLE DailySalesStatistics (
    date DATE PRIMARY KEY,
    total_sales DECIMAL(10,2),
    order_count INT,
    average_order_value DECIMAL(8,2)
);

컬럼에 중복과 계산 결과를 담는 경우

다른 테이블의 값을 함께 저장하면 조인을 줄일 수 있다. 주문 테이블에 고객명을 중복으로 보관하는 방식이 대표적이다.

정규화: 주문 테이블order_idcustomer_idorder_date반정규화정규화: 고객 테이블customer_idcustomer_nameemail반정규화: 주문 테이블order_idcustomer_idcustomer_nameorder_date

계산 비용이 큰 값은 파생 컬럼으로 저장할 수 있다. 주문의 총 금액을 별도 컬럼에 두는 예시는 다음과 같다.

ALTER TABLE Orders ADD total_amount DECIMAL(10,2);

변경 이력을 추적하려면 메타데이터 컬럼을 추가할 수 있다.

ALTER TABLE Products
ADD last_update_date DATETIME,
ADD last_updated_by VARCHAR(50);

조인 없이 조회할 수 있도록 PK 컬럼을 추가하는 방식도 컬럼 반정규화에 포함된다.

관계를 하나 더 만들어 조회 경로를 확보한다

테이블 간 접근 경로가 여러 개 필요하다면 중복 관계를 둘 수 있다. 고객에서 주문을 거쳐 상품으로 접근하던 구조에 고객과 상품 간 관계를 추가하는 방식이다.

중복 관계고객주문상품

상품 조회와 거래 이력에서의 적용

상품 상세 페이지가 상품, 카테고리, 판매자, 리뷰 정보를 조회하면서 다중 조인을 수행하면 로딩 성능이 떨어질 수 있다. 이 경우 상품 테이블에 카테고리명과 판매자명을 중복 저장하고, 평균 평점과 리뷰 수를 파생 컬럼으로 추가하며, 인기 상품을 위한 캐시 테이블을 별도로 둘 수 있다. 이 전략의 결과로 페이지 로딩 시간은 75% 감소하고 서버 부하는 60% 감소했다.

전자상거래 시스템에서 주문 내역을 조회할 때 주문상세와 상품 정보를 함께 표시하면 주문, 주문상세, 상품 테이블을 거치는 3중 조인이 성능 저하 요인이 될 수 있다. 이 경우 주문상세 테이블에 상품명, 가격처럼 자주 조회하는 상품 정보를 중복 저장할 수 있다. 사례에서는 조회 성능이 60% 향상됐고, 상품 정보가 바뀔 때 동기화 로직이 추가됐다.

금융 시스템에서 대용량 거래 이력을 조회하고 분석할 때도 반정규화를 적용할 수 있다. 거래 테이블을 연도별로 수평 분할하고, 빈번히 조회하는 계정 정보를 거래 테이블에 중복 저장하며, 일/월/연 단위 집계 통계 테이블을 생성하는 방식이다. 이력 조회 쿼리 성능은 90% 향상됐고 분석 작업 처리 시간은 80% 단축됐다.

대량 데이터를 기반으로 통계를 분석하는 데이터 웨어하우스에서는 복잡한 집계 연산이 응답 지연을 만들 수 있다. 사전 집계 테이블을 생성해 통계 값을 저장하면 분석 쿼리 응답 시간을 90% 단축할 수 있지만, 데이터 갱신 주기를 관리해야 한다.

적용 전 확인할 운영 조건

UPDATE와 INSERT가 빈번한 테이블은 반정규화 이후 오버헤드가 커질 수 있다. 반대로 SELECT 비중이 높은 테이블은 반정규화의 대상이 될 수 있다.

문서 지향 데이터베이스인 MongoDB 등은 중첩 문서 구조를 통해 데이터 접근을 최적화하는 반정규화 지향 특성을 가진다. 마이크로서비스 아키텍처에서도 서비스별 독립 데이터베이스 운영으로 데이터 중복이 발생하며, 이벤트 소싱과 CQRS 패턴은 이 환경에서 데이터 일관성을 관리하는 방법이 될 수 있다. 실시간 분석 환경에서는 스트리밍 데이터를 효율적으로 처리할 수 있는 데이터 구조가 필요하고, 반정규화는 실시간 처리 요구에 맞춰 데이터 접근을 조정하는 수단이 된다.

반정규화의 판단 기준은 데이터 특성과 사용 패턴이다. 성능 개선만을 위해 넓게 적용하면 일관성과 유지보수성이 흔들릴 수 있으므로, 비즈니스 요구사항과 시스템 특성에 맞는 균형을 찾아야 한다. 현재 데이터 규모뿐 아니라 앞으로의 데이터 볼륨과 성장률도 설계에 반영해야 하며, 대용량 데이터에서는 반정규화 효과가 크게 나타날 수 있다.

중복 데이터를 도입했다면 이를 일관되게 갱신할 장치가 필요하다. 트리거, 저장 프로시저, 배치 프로세스가 그 역할을 맡을 수 있다. 또한 적용 전후 성능을 객관적으로 측정하고, 여러 워크로드 상황에서 결과를 검증해야 한다.

중복 데이터를 갱신하는 구현 예시

카테고리명이 바뀔 때 상품 테이블의 중복 컬럼을 갱신하는 트리거는 다음과 같다.

CREATE TRIGGER update_product_category_name
AFTER UPDATE ON Categories
FOR EACH ROW
BEGIN
    UPDATE Products
    SET category_name = NEW.category_name
    WHERE category_id = NEW.category_id;
END;

일별 판매 통계를 갱신하는 저장 프로시저의 예시는 다음과 같다.

CREATE PROCEDURE update_daily_sales_statistics()
BEGIN
    INSERT INTO DailySalesStatistics
    SELECT
        DATE(order_date) as date,
        SUM(total_amount) as total_sales,
        COUNT(*) as order_count,
        AVG(total_amount) as average_order_value
    FROM Orders
    WHERE DATE(order_date) = DATE(NOW() - INTERVAL 1 DAY)
    GROUP BY DATE(order_date)
    ON DUPLICATE KEY UPDATE
        total_sales = VALUES(total_sales),
        order_count = VALUES(order_count),
        average_order_value = VALUES(average_order_value);
END;

연도별로 나눈 주문 테이블을 하나의 조회 대상으로 묶는 뷰는 다음과 같이 만들 수 있다.

CREATE VIEW all_orders AS
SELECT * FROM orders_2022
UNION ALL
SELECT * FROM orders_2023
UNION ALL
SELECT * FROM orders_2024;

병목에서 검증까지 이어지는 판단 과정

반정규화는 실제 성능 문제가 발생한 지점에서 시작해야 한다. 실행 계획 분석과 성능 프로파일링으로 병목을 찾고, 읽기와 쓰기 비율 및 반복되는 쿼리 패턴을 확인한다. 그 다음 개발·유지보수 비용과 성능 개선 효과, 데이터 정합성 위험과 사용자 경험 향상을 함께 비교한다.

효과가 큰 부분부터 점진적으로 적용하고, 각 단계에서 성능을 측정해 검증하는 흐름이 필요하다.

읽기 위주쓰기 위주아니오성능 문제 식별데이터 접근 패턴 분석읽기/쓰기 비율?반정규화 고려정규화 유지후보 기법 식별비용-효익 분석테스트 환경 구현성능 측정성능 개선?실제 환경 적용

반정규화의 가치는 중복 자체에 있지 않다. 시스템 요구사항, 데이터 특성, 사용 패턴을 기준으로 정합성 관리 비용보다 조회 성능 개선의 이점이 큰 지점을 선택하는 데 있다.

반정규화데이터베이스 설계쿼리 최적화데이터 정합성성능 최적화