핵심 인사이트
- 행 지향 저장소(Row-Oriented Store)는 한 행의 모든 컬럼 데이터를 연속 저장 — 특정 행의 전체 속성을 한 번에 읽는 OLTP(Online Transaction Processing) 워크로드에 최적화되어 있으며, INSERT/UPDATE/DELETE 시 단일 I/O로 행 전체를 처리한다.
- OLTP와 OLAP는 완전히 다른 최적화 방향 — OLTP는 행 지향(단일 행 빠른 접근), OLAP는 컬럼 지향(대용량 컬럼 집계). 같은 물리적 데이터를 두 방향으로 저장하는 HTAP(Hybrid)가 현대 트렌드다.
- 버퍼 풀(Buffer Pool)이 OLTP 성능의 핵심 — 행 지향 DB의 메인 성능 메커니즘은 자주 쓰는 페이지를 메모리에 캐싱하는 버퍼 풀이며, 버퍼 풀 히트율 99% 이상이 고성능 OLTP의 목표다.
Ⅰ. 행 지향 저장소 구조
행 지향 (Row-Oriented) 저장:
테이블 데이터:
ID 이름 연봉 부서
1 김철수 5000 개발
2 이영희 6000 마케팅
3 박민수 4500 개발
디스크 저장 순서:
[1|김철수|5000|개발] [2|이영희|6000|마케팅] [3|박민수|4500|개발]
└──── 1페이지(8KB) ────┘
특성:
한 행의 모든 컬럼 = 연속된 바이트
행 단위 I/O = 페이지 1번 읽어 전체 컬럼 접근
OLTP에서의 장점:
SELECT * WHERE id = 1
→ 행 1 포함 페이지 1개만 읽기 (빠름)
UPDATE salary WHERE id = 1
→ 행 1 포함 페이지 읽기 → 수정 → 쓰기
OLAP에서의 단점:
SELECT SUM(연봉) FROM employees
→ 모든 행 전체 컬럼 읽기 (이름, 부서 불필요)
→ 불필요한 컬럼 I/O 발생
페이지 구조 (MySQL InnoDB):
페이지 크기: 16KB (기본)
페이지 내 구조:
[페이지 헤더][행1][행2]...[행N][빈 공간][페이지 디렉토리][페이지 트레일러]
가득 찬 페이지(Fill Factor ~87%):
INSERT 공간 부족 → 페이지 분할 (Page Split)
📢 섹션 요약 비유: 행 지향 = 서랍 한 칸에 한 사람 정보 — "김철수" 서랍엔 이름+연봉+부서 한 번에. 한 명 정보 꺼낼 때(OLTP) 빠름. 전체 연봉 합산(OLAP) 땐 모든 서랍 열어야 해서 느림!
Ⅱ. OLTP 특성과 최적화
OLTP (Online Transaction Processing):
특성:
짧고 빠른 트랜잭션 (< 100ms)
대량의 동시 사용자 (수천~수만)
단일 행 또는 소수 행 접근
INSERT/UPDATE/DELETE 빈번
ACID 트랜잭션 필수
워크로드 예:
은행 계좌 이체
전자상거래 주문 처리
좌석 예약 시스템
재고 관리
OLTP 최적화 기법:
1. 버퍼 풀 (Buffer Pool):
자주 접근하는 페이지 메모리 캐싱
히트율 = 메모리에서 직접 읽기 비율
목표: 99%+ 히트율
LRU 알고리즘으로 페이지 교체
버퍼 풀 크기 = 물리 메모리 × 70~80%
2. 인덱스 (Index):
B+ 트리 인덱스: 범위 검색 효율
복합 인덱스: 자주 쓰는 WHERE 컬럼 조합
쓰기 오버헤드:
INSERT 1건 → 인덱스 수 × 2 I/O 추가
3. 연결 풀 (Connection Pool):
DB 연결 생성 비용 절감
PgBouncer, HikariCP
4. 파티셔닝:
날짜 범위 파티션
오래된 파티션 DROP → 빠른 삭제
5. 읽기 복제본 (Read Replica):
Write → Primary
Read → Replica (SELECT 부하 분산)
📢 섹션 요약 비유: OLTP 최적화 = 편의점 운영 — 잘 팔리는 물건(버퍼 풀: 자주 쓰는 페이지 캐시), 빠른 검색(인덱스), 여러 계산대(연결 풀), 창고 분리(파티셔닝)!
Ⅲ. OLTP vs OLAP 비교
비교표:
특성 | OLTP | OLAP
-------------|---------------------|-------------------
목적 | 업무 처리 | 분석, 의사결정
쿼리 유형 | 단순 DML/PK 조회 | 복잡 집계, JOIN
행 접근 | 수~수십 행 | 수백만~수십억 행
응답 시간 | 밀리초 | 초~분
동시 사용자 | 수천~수만 | 수십~수백
데이터 갱신 | 실시간 (빈번) | 배치 (드문)
최적화 | 행 지향 저장 | 컬럼 지향 저장
대표 DB | MySQL, PostgreSQL | Snowflake, BigQuery
트랜잭션 | ACID 필수 | 완화 가능
HTAP (Hybrid Transaction/Analytical Processing):
단일 DB에서 OLTP + OLAP 동시 처리
기술:
인메모리 컬럼 저장소:
TiDB: RocksDB(OLTP) + TiFlash(OLAP)
MySQL HeatWave: InnoDB + 컬럼 가속기
SQL Server: In-Memory OLTP + Columnstore
장점:
ETL 불필요 (OLTP DB에서 직접 분석)
데이터 신선도: 실시간 분석
단점:
자원 경합 (OLTP-OLAP I/O 충돌)
복잡한 운영
실제 아키텍처 선택:
소규모: HTAP 가능 (MySQL HeatWave)
중규모: OLTP DB + 야간 ETL + OLAP DB (Redshift)
대규모: OLTP (Aurora) + 스트리밍 CDC + 실시간 OLAP (ClickHouse)
📢 섹션 요약 비유: OLTP vs OLAP = 편의점 POS vs 마케팅 분석팀 — POS(OLTP)는 1건 빠르게 처리, 마케팅(OLAP)은 전체 데이터 집계. HTAP은 같은 데이터로 두 가지 모두!
Ⅳ. InnoDB 행 지향 구현
MySQL InnoDB 행 저장 상세:
B+ 트리 클러스터드 인덱스:
기본 키 순서로 행 저장
장점: PK 기반 검색 = 인덱스 + 데이터 1번 I/O
단점: 무작위 PK INSERT = 페이지 분할 빈번
물리적 행 형식 (COMPACT):
[삭제 플래그 1비트][레코드 타입][N-byte 포인터][NULL 비트맵]
[가변 길이 컬럼 오프셋 목록][컬럼1][컬럼2]...[컬럼N]
가변 길이 컬럼 (VARCHAR):
데이터 앞에 실제 길이 저장
VARCHAR(255) → 최대 1바이트 오프셋
VARCHAR(65535) → 최대 2바이트 오프셋
페이지 분할 (Page Split):
B+ 트리 노드 가득 참 → 분할
순서 INSERT (PK 1, 2, 3, ...): 분할 적음
무작위 INSERT (UUID): 잦은 분할 → 성능 저하
해결: UUID v7 (시간 순서 보장)
또는 AUTO_INCREMENT + 순서 삽입
MVCC (Multi-Version Concurrency Control):
행 변경 시 기존 행 삭제 안 함
행에 trx_id, roll_pointer 포함
읽기: 자신의 트랜잭션 시작 전 버전 읽기
Undo Log: 이전 버전 저장 공간
장기 트랜잭션 → Undo Log 급증 → 성능 저하
📢 섹션 요약 비유: InnoDB 행 저장 = 파일 캐비닛 정리 — PK 순서로 서랍(페이지) 정렬. UUID로 랜덤 저장하면 서랍 분할(Page Split) 잦아 혼란. 순서대로 넣어야 빠름!
Ⅴ. 실무 시나리오 — 전자상거래 OLTP 최적화
전자상거래 주문 시스템 OLTP 튜닝:
현황:
MySQL 8.0 (InnoDB)
초당 주문: 1,000건
평균 응답: 250ms (목표 50ms)
슬로우 쿼리:
SELECT * FROM orders WHERE user_id = ? AND status = 'pending'
→ 200ms (풀 스캔)
분석:
orders 테이블: 5천만 행
인덱스: PK (order_id) 만 존재
쿼리 플랜:
→ type: ALL (풀 테이블 스캔!)
→ rows: 50,000,000 (전체 스캔)
최적화:
1. 복합 인덱스 추가:
CREATE INDEX idx_user_status
ON orders (user_id, status, created_at DESC);
쿼리 플랜 재확인:
→ type: ref (인덱스 사용)
→ rows: 15 (극적 감소)
응답: 200ms → 8ms ✓
2. 버퍼 풀 증설:
innodb_buffer_pool_size = 4G → 12G
히트율: 92% → 99.3%
3. 읽기 복제본 추가:
주문 조회 API → 복제본으로 라우팅
Primary 쓰기 부하 40% 감소
4. Connection Pool 최적화:
HikariCP maxPoolSize = 50 → 100
connectionTimeout = 30s → 5s
결과:
평균 응답: 250ms → 35ms (7배 개선)
P99: 800ms → 120ms
TPS: 1,000 → 3,500
교훈:
인덱스 설계가 OLTP 성능의 80% 결정
버퍼 풀 크기 = 활성 데이터 셋 크기가 이상적
📢 섹션 요약 비유: OLTP 튜닝 결과 — 인덱스 없어서 5천만 행 전체 스캔(200ms)! 복합 인덱스 추가로 15행만 읽기(8ms). 인덱스 하나로 25배 빠르게. OLTP는 인덱스 설계가 90%!
📌 관련 개념 맵
행 지향 저장소 / OLTP
+-- 저장 구조
| +-- 행 단위 연속 저장
| +-- B+ 트리 클러스터드 인덱스
| +-- 페이지 (8~16KB)
+-- OLTP 최적화
| +-- 버퍼 풀 (Buffer Pool)
| +-- 인덱스 설계
| +-- Connection Pool
| +-- 읽기 복제본
+-- 비교
| +-- OLAP (컬럼 지향)
| +-- HTAP (하이브리드)
+-- 대표 DB
+-- MySQL InnoDB
+-- PostgreSQL
+-- Oracle
📈 관련 키워드 및 발전 흐름도
[파일 기반 DB (1960s)]
레코드 순차 저장
행 지향의 원형
|
v
[관계형 DB (1970s)]
IBM System R
InnoDB 전신 페이지 구조
|
v
[OLTP 최적화 (1980~90s)]
버퍼 풀, B+ 트리 표준화
오라클, SQL Server
|
v
[인메모리 DB (2000s~)]
SAP HANA, VoltDB
RAM = 디스크 대체
|
v
[HTAP (2015~)]
TiDB, MySQL HeatWave
OLTP + OLAP 통합
|
v
[현재: 클라우드 OLTP]
Aurora, Cloud Spanner
서버리스 OLTP
👶 어린이를 위한 3줄 비유 설명
- 행 지향 = 서랍 한 칸에 한 사람 정보 — 서랍 열면 이름+연봉+부서 한꺼번에. 한 명 찾을 때(OLTP) 빠르고, 전체 연봉 합산(OLAP)엔 느려요!
- 버퍼 풀 = 책상 위 자주 쓰는 서류 — 자주 꺼내는 서랍(페이지)을 책상(메모리)에 올려두기. 99% 히트율 = 창고 안 가도 됨!
- 인덱스 = 목차 — 5천만 페이지 책에서 목차 없이 찾기(풀 스캔: 200ms) vs 목차로 찾기(인덱스: 8ms). 25배 빠름!