컴퓨터 구조 운영체제 네트워크 자료구조 알고리즘 데이터베이스 내 학습
db · 19

OLAP vs OLTP

데이터웨어하우스OLTPOLAP

OLTP(Online Transaction Processing)는 주문, 결제, 재고 갱신처럼 짧고 빈번한 읽기·쓰기 트랜잭션을 빠르고 안정적으로 처리하는 업무입니다. 반대로 OLAP(Online Analytical Processing)은 대량의 이력 데이터를 여러 기준으로 집계하고 분석하는, 읽기 중심의 업무입니다.

두 업무는 쿼리의 모양이 달라서 데이터베이스의 설계와 저장 방식도 다르게 가져갑니다. OLTP는 행 하나를 다루는 일이 많아 정규화된 스키마와 인덱스, 행 지향 저장이 어울리고, OLAP은 일부 열을 많은 행에 걸쳐 읽는 일이 많아 컬럼 지향 저장이 유리합니다. 이 토픽에서는 두 업무의 차이와 저장 방식의 차이를 시각화로 확인합니다.

핵심 개념

OLTP

짧은 읽기·쓰기 트랜잭션을 많은 사용자가 동시에 실행합니다. 행 단위 접근이 많고 일관성과 동시성이 중요합니다.

핵심 개념

OLAP

대량의 이력 데이터를 집계하고 분석합니다. 복잡한 읽기 쿼리가 중심이고 한 쿼리가 많은 행을 훑습니다.

핵심 개념

데이터 웨어하우스

여러 업무 시스템의 데이터를 모아 분석용으로 저장하는 저장소입니다. OLAP 분석이 주로 여기서 이루어집니다.

핵심 개념

행 지향 저장

한 행의 값을 이어 붙여 저장합니다. 행 하나를 읽고 쓰기에 좋아 OLTP에 어울립니다.

핵심 개념

컬럼 지향 저장

한 열의 값을 모아 저장합니다. 필요한 열만 읽고 같은 열끼리 압축이 잘 되어 OLAP에 유리합니다.

핵심 개념

ETL

업무 시스템의 데이터를 추출(Extract)하고 변환(Transform)해 웨어하우스에 적재(Load)하는 과정입니다. 분석 부하를 업무 DB와 분리합니다.

쿼리의 모양이 다릅니다 — OLTP는 소수의 행을 인덱스로 찾아 읽고 쓰고, OLAP은 많은 행을 훑어 소수의 요약 행을 만듭니다.
분석이 업무를 방해할 수 있습니다 — 오래 걸리는 집계 쿼리를 업무용 DB에서 돌리면 자원을 차지해 주문과 결제의 응답에 영향을 줄 수 있어, 분석을 따로 분리합니다.
저장 방식이 읽는 양을 바꿉니다 — 필요한 열이 일부일 때 컬럼 지향은 그 열만 읽어 I/O를 줄이고, 행 하나만 다룰 때는 행 지향이 유리합니다.
정답은 둘 중 하나가 아닙니다 — 실제 시스템은 업무 DB와 분석 저장소를 함께 두거나, 한 제품이 두 부하를 모두 다루도록 만들기도 합니다.
이 시각화에서 확인할 것 — "OLTP" 탭에서 INSERT, UPDATE, SELECT가 행 하나만 건드리는 모습을, "OLAP" 탭에서 모든 행의 region과 amount 열을 읽어 3행으로 집계하는 모습을, "행 vs 컬럼 저장" 탭에서 같은 쿼리가 행 지향에서는 30칸, 컬럼 지향에서는 12칸을 읽는 모습을 STEP으로 확인하세요.

OLTP와 OLAP의 배경

OLAP이라는 용어는 1993년 E. F. 코드(E. F. Codd) 등이 쓴 백서에서 사용되었습니다. 앞서 1992년에는 빌 인먼(Bill Inmon)이 『Building the Data Warehouse』에서 데이터 웨어하우스를 주제 지향, 통합, 시간 가변, 비휘발성 데이터 모음으로 정리했고, 이후 업무 처리와 분석을 나누는 구조가 널리 쓰였습니다.

컬럼 지향 저장은 분석 부하에 맞춰 발전했습니다. 2005년 스톤브레이커(M. Stonebraker) 등이 발표한 C-Store 논문이 읽기에 최적화한 컬럼 지향 DBMS의 설계를 제시했고(그 이전에도 컬럼 저장을 쓰는 제품이 있었습니다), 이후 분석용 데이터베이스와 저장 형식에 널리 쓰이고 있습니다.

숫자로 확인하기

OLTP는 행 1개 — 주문 6건이 든 표에서 INSERT, UPDATE, SELECT가 각각 건드리는 행은 6행 중 1행입니다.
OLAP은 전체 행 — 지역별 합계는 6행 모두에서 region과 amount 2개 열을 읽어 KR 350, US 450, JP 80의 3행을 만듭니다.
읽는 칸 30 대 12 — 행 지향 저장은 행 전체인 6 × 5 = 30칸을 읽고 컬럼 지향은 필요한 2개 열의 6 × 2 = 12칸을 읽습니다. 실제 엔진은 칸이 아닌 블록 단위로 읽으므로 이 비율은 장난감 표의 수치입니다.
코드 확인 — 파이썬 모델은 읽는 칸 30과 12, 집계 {'KR': 350, 'US': 450, 'JP': 80}을 출력하고, 자바 모델은 {JP=80, KR=350, US=450}과 amount 열 합계 880을 출력합니다.

자주 하는 오해

OLAP은 쓰기를 전혀 하지 않는다 — 읽기가 중심일 뿐이고, 웨어하우스에는 ETL(또는 ELT)이 보통 배치로 대량의 데이터를 적재합니다. 스트리밍으로 적재하는 시스템도 있습니다.
OLAP에는 인덱스가 필요 없다 — 컬럼 지향과 파티셔닝 같은 방식이 중심이어도 쿼리에 따라 인덱스나 요약 구조를 쓸 수 있습니다.
컬럼 지향이 항상 더 좋다 — 필요한 열이 일부이고 많은 행을 읽을 때 유리합니다. 행 하나를 읽고 쓰는 OLTP에서는 열마다 따로 접근해야 해서 불리합니다.
OLTP 데이터베이스로 분석까지 해도 된다 — 작은 규모에서는 가능하지만 무거운 집계가 업무 쿼리와 자원을 다투면 응답이 느려질 수 있어 분리를 검토합니다.
OLAP은 정규화된 스키마를 쓴다 — OLTP는 중복을 줄이려 정규화하지만 분석용 스키마는 조회와 집계를 쉽게 하려고 의도적으로 비정규화하는 경우가 많습니다.

면접에서 이렇게 물어요

면접 질문

OLTP의 특징은?

주문과 결제처럼 짧고 빈번한 트랜잭션을 빠르고 안정적으로 처리하는 데 초점을 둡니다. 정규화된 스키마와 인덱스를 사용합니다.

면접 질문

OLAP의 특징은?

대량의 이력 데이터를 집계하고 분석하는 읽기 중심 쿼리를 다룹니다. 데이터 웨어하우스에서 주로 사용합니다.

면접 질문

컬럼 지향 저장이 OLAP에 유리한 이유는?

분석 쿼리는 일부 열을 많은 행에 걸쳐 읽으므로 필요한 열만 읽어 I/O를 줄일 수 있고, 같은 열의 값끼리 압축 효율도 높일 수 있습니다.

면접 질문

분석 쿼리를 왜 업무 DB에서 따로 떼어 내나요?

오래 걸리는 집계 쿼리가 업무 DB의 자원을 차지해 주문과 결제 응답에 영향을 줄 수 있어서, 분석 전용 저장소로 데이터를 옮겨 실행합니다.

활용 분야

업무 시스템 — 쇼핑몰의 주문, 결제, 재고, 은행의 이체처럼 정확성과 동시성이 중요한 서비스는 OLTP입니다.
리포트와 대시보드 — 기간별, 지역별 매출 집계처럼 이력 데이터를 분석하는 작업은 OLAP입니다.
분석 파이프라인 — ETL로 업무 DB의 데이터를 웨어하우스로 옮기고, 웨어하우스에서 집계해 의사 결정에 씁니다.

함께 보면 좋은 토픽

트랜잭션 — OLTP가 지켜야 하는 ACID를 봅니다.
정규화 — OLTP 스키마가 중복을 줄이는 방식을 봅니다.
인덱스 — OLTP에서 행 하나를 빨리 찾는 방법을 봅니다.
테이블 파티셔닝 — 큰 이력 테이블을 나눠 분석 쿼리가 읽는 범위를 줄이는 방법을 봅니다.

참고 자료

논문 — Stonebraker, M. et al., "C-Store: A Column-oriented DBMS", VLDB 2005
논문 — Abadi, D. et al., "The Design and Implementation of Modern Column-Oriented Database Systems", Foundations and Trends in Databases 5(3), 2012 (© 2013), pp. 197–280
논문 — Codd, E. F., Codd, S. B., Salley, C. T., "Providing OLAP (On-line Analytical Processing) to User-Analysts: An IT Mandate", 1993
공식 문서 — PostgreSQL: Transactions (Tutorial) (SQL 코드의 BEGIN과 COMMIT 근거)

SQL — OLTP의 트랜잭션과 OLAP의 집계 쿼리

-- OLTP: 짧은 트랜잭션, 행 하나씩
BEGIN;
INSERT INTO sales (id, sold_on, region, product, amount)
VALUES (7, '2024-10-04', 'KR', 'A', 120);
UPDATE stock SET quantity = quantity - 1 WHERE product = 'A';
COMMIT;

SELECT * FROM sales WHERE id = 7;                    -- 기본 키 인덱스를 써서 행 하나를 찾을 수 있다

-- OLAP: 많은 행을 훑어 집계
SELECT region, date_trunc('month', sold_on) AS month, SUM(amount) AS total
FROM sales
WHERE sold_on >= '2022-01-01'
GROUP BY region, month
ORDER BY region, month;                              -- 지난 몇 해의 지역별 월 매출

위쪽은 OLTP의 전형적인 모습입니다. 주문 한 건을 INSERT하고 재고를 UPDATE한 뒤 바로 커밋하는 짧은 트랜잭션이고, 조회도 기본 키 인덱스를 써서 행 하나를 찾을 수 있습니다. 시각화의 OLTP 탭이 보여 주는 INSERT, UPDATE, SELECT와 같은 흐름이며, 시각화는 표의 행 하나를 예로 강조합니다. date_trunc와 GROUP BY에서 출력 열 별칭을 쓰는 것은 PostgreSQL 기준 문법이고 제품마다 다릅니다. 아래쪽은 OLAP의 전형적인 모습입니다. 오랜 기간의 많은 행을 훑어 지역과 월별로 묶어 합계를 구합니다. 결과는 요약된 소수의 행이지만 읽는 행은 매우 많아 쿼리 하나가 오래 걸릴 수 있어, 업무 처리용 DB와 분리한 데이터 웨어하우스에서 돌리는 경우가 많습니다. 테이블과 열 이름은 예시입니다.

Python — 행 지향과 컬럼 지향이 읽는 양

rows = [(1, '10-01', 'KR', 'A', 100), (2, '10-01', 'US', 'B', 150), (3, '10-02', 'KR', 'A', 200),
        (4, '10-02', 'JP', 'C', 80), (5, '10-03', 'US', 'B', 300), (6, '10-03', 'KR', 'A', 50)]
names = ('id', 'date', 'region', 'product', 'amount')

cols = {n: [r[i] for r in rows] for i, n in enumerate(names)}   # 컬럼 지향: 열마다 모아 저장

need = ('region', 'amount')                                      # 집계 쿼리가 필요한 열
row_cells = len(rows) * len(names)                               # 행 지향: 행 전체를 읽음
col_cells = len(rows) * len(need)                                # 컬럼 지향: 필요한 열만 읽음
print('읽는 셀:', row_cells, col_cells)

total = {}
for region, amount in zip(cols['region'], cols['amount']):
    total[region] = total.get(region, 0) + amount
print('집계:', total)

print('행 하나(id=3) 읽기:', rows[2])                            # 행 지향: 한 번에 연속으로
print('컬럼 지향으로 id=3 읽기:', [cols[n][2] for n in names], '열 5곳을 읽음')
# 읽는 셀: 30 12
# 집계: {'KR': 350, 'US': 450, 'JP': 80}
# 행 하나(id=3) 읽기: (3, '10-02', 'KR', 'A', 200)
# 컬럼 지향으로 id=3 읽기: [3, '10-02', 'KR', 'A', 200] 열 5곳을 읽음

같은 6행을 행 지향(rows)과 컬럼 지향(cols)으로 놓고 읽는 양을 비교하는 장난감 모델입니다. 지역별 합계에는 region과 amount 두 열만 필요하므로, 컬럼 지향은 12칸만 읽고 행 지향은 행 전체인 30칸을 읽습니다. 시각화의 행 vs 컬럼 저장 탭과 같은 결과입니다. 집계 결과는 KR 350, US 450, JP 80입니다. 반대로 id가 3인 행 하나를 읽는 경우, 행 지향은 한 곳에서 5개의 값을 한꺼번에 읽지만 컬럼 지향은 5개의 열을 각각 찾아가야 합니다. 실제 컬럼 지향 저장소는 열을 압축하고 블록 단위로 읽으며 훨씬 복잡합니다.

Java — 스트림 집계와 열 단위 배열

record Sale(int id, String date, String region, String product, int amount) {}

List<Sale> rows = List.of(
    new Sale(1, "10-01", "KR", "A", 100), new Sale(2, "10-01", "US", "B", 150),
    new Sale(3, "10-02", "KR", "A", 200), new Sale(4, "10-02", "JP", "C", 80),
    new Sale(5, "10-03", "US", "B", 300), new Sale(6, "10-03", "KR", "A", 50));

Map<String, Integer> byRegion = rows.stream()
    .collect(Collectors.groupingBy(Sale::region, TreeMap::new, Collectors.summingInt(Sale::amount)));
System.out.println("집계: " + byRegion);                           // 집계: {JP=80, KR=350, US=450}

int[] amount = rows.stream().mapToInt(Sale::amount).toArray();      // 컬럼 지향: amount 열만 모은 배열
System.out.println("amount 열: " + Arrays.toString(amount) + " 합계 " + Arrays.stream(amount).sum());
// amount 열: [100, 150, 200, 80, 300, 50] 합계 880

자바에서는 레코드 목록(행 지향)에 스트림으로 groupingBy와 summingInt를 적용해 지역별 합계를 구합니다. TreeMap을 써서 키 순서로 JP, KR, US가 출력되고 값은 파이썬 결과와 같습니다. 이 방식은 Sale 객체를 하나씩 따라가며 region과 amount를 읽습니다. 아래쪽처럼 amount만 모은 배열로 합계를 구하면 필요한 열만 읽는 컬럼 지향의 모습을 흉내 낼 수 있고, 합계 880은 세 지역의 합 350 + 450 + 80과 같습니다. 많은 분석 엔진은 열 단위 저장과 압축을 쓰고, 일부는 벡터화된 계산도 함께 씁니다. 이 코드는 개념 설명용입니다.

Q1

OLTP(Online Transaction Processing)의 특징은?

쇼핑몰의 주문 처리 시스템을 떠올려 보세요.
OLTP는 주문 접수, 결제, 재고 갱신처럼 짧은 읽기/쓰기 트랜잭션을 대량으로 빠르게 처리하는 시스템입니다. 정규화된 스키마와 인덱스를 사용합니다.

Q2

OLAP(Online Analytical Processing)의 특징은?

"지난 3년간 지역별 매출 합계" 같은 질문에 답합니다.
OLAP은 대규모 데이터를 여러 차원으로 집계하고 분석하는 시스템으로, 복잡한 읽기 쿼리가 중심이며 데이터 웨어하우스에서 주로 사용합니다.

Q3

컬럼 지향(Columnar) 저장 방식이 OLAP에 유리한 이유는?

100개 컬럼 중 2개만 집계하는 쿼리를 생각해 보세요.
분석 쿼리는 보통 소수의 컬럼을 대량의 행에 걸쳐 읽습니다. 컬럼 단위로 저장하면 필요한 컬럼만 읽고, 같은 타입 값이 모여 있어 압축도 잘 됩니다.