1. 개요

1.1 RDBMS 시장 현황

관계형 데이터베이스 관리 시스템(RDBMS)은 수십 년간 데이터 저장의 근간을 이루어 왔다. 2024년 기준 DB-Engines 랭킹 기준 상위 RDBMS는 Oracle, MySQL, SQL Server, PostgreSQL, MariaDB 순이다. NoSQL의 성장에도 불구하고 RDBMS는 여전히 전체 데이터베이스 시장의 60% 이상을 차지하며, 특히 금융, 제조, 공공 부문에서 압도적인 점유율을 유지한다.

클라우드 전환 이후 Amazon Aurora, Google Cloud SQL, Azure Database 등 매니지드 서비스가 급성장하면서 설치형(on-premise) RDBMS의 운영 부담이 크게 줄었고, 이로 인해 오픈소스 RDBMS(MySQL, PostgreSQL)의 채택률이 더욱 높아졌다.

1.2 각 DB의 역사와 라이선스

데이터베이스 최초 출시 개발사/관리 주체 라이선스
MySQL 1995 Oracle Corporation GPL v2 / 상용 이중 라이선스
PostgreSQL 1996 (Postgres 1986) PostgreSQL Global Development Group PostgreSQL License (BSD 계열)
Oracle Database 1979 Oracle Corporation 상용 (Enterprise/Standard)
MariaDB 2009 MariaDB Foundation / MariaDB plc GPL v2
SQL Server 1989 Microsoft 상용 (Express 무료 제한판 있음)

MySQL 은 1995년 스웨덴의 MySQL AB가 개발하였고, 2008년 Sun Microsystems, 2010년 Oracle이 인수하였다. GPL v2 라이선스이지만 상용 라이선스를 별도 판매하는 이중 라이선스 구조이다.

PostgreSQL 은 UC Berkeley의 POSTGRES 프로젝트(1986)에서 출발하여 1996년 현재의 이름으로 공개되었다. BSD 계열의 PostgreSQL License로 배포되어 수정·재배포·상업적 이용이 매우 자유롭다.

Oracle Database 는 최초의 상용 RDBMS 중 하나로 Larry Ellison이 1977년 설립한 SDL(현 Oracle)이 개발하였다. 현재까지도 엔터프라이즈 시장에서 점유율 1위를 유지하며 가장 고가의 RDBMS이다.

MariaDB 는 Oracle의 MySQL 인수 이후 MySQL 공동 창업자 Monty Widenius가 MySQL 5.1을 fork하여 2009년 출시하였다. MySQL과 높은 호환성을 유지하면서 독자적인 스토리지 엔진(Aria, ColumnStore 등)을 추가하였다.

SQL Server 는 Microsoft가 Sybase와 공동 개발하여 1989년 출시하였다. 2016년부터 Linux 지원이 추가되었고, Azure SQL Database로 클라우드 서비스도 제공한다.


2. 아키텍처 비교

2.1 전체 아키텍처 개요

┌─────────────────────────────────────────────────────────────────────┐
│                    RDBMS 계층 구조 비교                              │
├─────────────┬─────────────┬─────────────┬─────────────┬────────────┤
│   MySQL     │ PostgreSQL  │   Oracle    │  MariaDB    │ SQL Server │
├─────────────┼─────────────┼─────────────┼─────────────┼────────────┤
│ Client Conn │ Client Conn │ Client Conn │ Client Conn │ Client Conn│
│ Thread Pool │ Process per │ Dedicated/  │ Thread Pool │ Thread Pool│
│             │ Connection  │ Shared Srv  │             │            │
├─────────────┼─────────────┼─────────────┼─────────────┼────────────┤
│ Query Cache │ Query Plan  │ Library     │ Query Cache │ Plan Cache │
│ (deprecated)│ Cache       │ Cache       │ (optional)  │            │
├─────────────┼─────────────┼─────────────┼─────────────┼────────────┤
│  Parser /   │  Parser /   │  Parser /   │  Parser /   │  Parser /  │
│  Optimizer  │  Optimizer  │  Optimizer  │  Optimizer  │  Optimizer │
├─────────────┼─────────────┼─────────────┼─────────────┼────────────┤
│  InnoDB /   │  Heap-based │  Tablespace │  InnoDB /   │  Extent-   │
│  MyISAM 등  │  Storage    │  Storage    │  Aria 등    │  based     │
│  (플러그인) │             │             │  (플러그인) │  Storage   │
├─────────────┼─────────────┼─────────────┼─────────────┼────────────┤
│ Buffer Pool │Shared Buffer│  SGA / PGA  │ Buffer Pool │Buffer Pool │
│             │             │ (Buffer     │             │            │
│             │             │  Cache)     │             │            │
└─────────────┴─────────────┴─────────────┴─────────────┴────────────┘

2.2 스토리지 엔진

InnoDB (MySQL / MariaDB)

InnoDB는 MySQL의 기본 스토리지 엔진으로 클러스터드 인덱스(Clustered Index)를 사용한다. Primary Key 순서로 데이터가 물리적으로 정렬되어 저장되며, 모든 Secondary Index는 Primary Key 값을 포함한다.

InnoDB 파일 구조:
┌──────────────────────────────────────────────┐
│  ibdata1 (System Tablespace)                 │
│  ├── Data Dictionary                         │
│  ├── Undo Logs (undo tablespace 분리 가능)   │
│  └── Double Write Buffer                     │
├──────────────────────────────────────────────┤
│  table.ibd (Per-Table Tablespace)            │
│  ├── Leaf Pages (실제 행 데이터)             │
│  └── Non-Leaf Pages (인덱스 노드)            │
├──────────────────────────────────────────────┤
│  ib_logfile0, ib_logfile1 (Redo Log)         │
└──────────────────────────────────────────────┘

PostgreSQL Heap 구조

PostgreSQL은 힙(Heap) 기반으로 데이터를 저장한다. 테이블 파일은 8KB 페이지(블록)로 구성되며, 인덱스와 테이블이 완전히 분리된 구조이다. MVCC를 위해 동일 테이블 내에 여러 버전의 튜플을 함께 저장한다.

PostgreSQL 파일 구조:
PGDATA/
├── base/
│   └── {db_oid}/
│       ├── {relation_oid}       ← 테이블 힙 파일
│       ├── {relation_oid}_fsm   ← Free Space Map
│       ├── {relation_oid}_vm    ← Visibility Map
│       └── {index_oid}          ← 인덱스 파일
├── pg_wal/                      ← WAL (Write-Ahead Log)
├── pg_undo/                     ← (없음, 힙 내 버전 관리)
└── global/                      ← 공유 시스템 카탈로그

Oracle Tablespace

Oracle은 테이블스페이스(Tablespace) → 세그먼트(Segment) → 익스텐트(Extent) → 데이터 블록(Data Block)의 4단계 계층 구조로 저장 공간을 관리한다.

Oracle 스토리지 계층:
Tablespace (논리)
└── Segment (테이블, 인덱스, Undo 등)
    └── Extent (연속된 블록 집합)
        └── Oracle Block (2KB~32KB, 기본 8KB)
            ├── Block Header
            ├── Table Directory
            ├── Row Directory
            └── Row Data (가변)

2.3 프로세스 모델

프로세스/스레드 모델 비교:

MySQL / MariaDB / SQL Server:
┌─────────────────────────────────────┐
│           메인 프로세스              │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐       │
│  │ T1 │ │ T2 │ │ T3 │ │ T4 │ ...  │  ← 연결당 스레드
│  └────┘ └────┘ └────┘ └────┘       │
│  (Thread Pool로 스레드 재사용 가능)  │
└─────────────────────────────────────┘
장점: 낮은 연결 오버헤드, 공유 메모리 효율적
단점: 한 스레드 문제가 전체 프로세스에 영향

PostgreSQL:
┌────────────────────────────────────┐
│  postmaster (마스터 프로세스)       │
│    ↓ fork()                         │
│  ┌──────┐ ┌──────┐ ┌──────┐       │
│  │ Proc1│ │ Proc2│ │ Proc3│ ...   │  ← 연결당 프로세스
│  └──────┘ └──────┘ └──────┘       │
└────────────────────────────────────┘
장점: 프로세스 격리로 안정성 높음
단점: 연결 수 증가 시 메모리 사용 급증 → PgBouncer 권장

Oracle:
┌─────────────────────────────────────────┐
│  Dedicated Server Mode:                  │
│  클라이언트 1 → Server Process 1 (1:1)  │
│                                          │
│  Shared Server Mode (MTS):               │
│  클라이언트 N → Dispatcher → Shared Srv  │
│               ↓                          │
│          Request Queue                   │
│               ↓                          │
│          Shared Server 풀                │
└─────────────────────────────────────────┘

2.4 메모리 구조

MySQL Buffer Pool

MySQL InnoDB 메모리 구조:
┌─────────────────────────────────────────────┐
│              Buffer Pool                     │
│  ┌─────────────┬────────────────────────┐   │
│  │   New       │        Old             │   │
│  │ Sublist(5/8)│    Sublist(3/8)        │   │
│  │ (MRU end)   │    (LRU end)           │   │
│  └─────────────┴────────────────────────┘   │
│  ┌────────────────────────────────────────┐  │
│  │   Change Buffer (DML 변경 버퍼링)      │  │
│  └────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────┐  │
│  │   Adaptive Hash Index                  │  │
│  └────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────┐  │
│  │   Log Buffer (Redo Log 버퍼)           │  │
│  └────────────────────────────────────────┘  │
└─────────────────────────────────────────────┘
  • innodb_buffer_pool_size: 전체 메모리의 70~80% 권장
  • 여러 인스턴스(innodb_buffer_pool_instances)로 분할하여 경합 감소 가능

PostgreSQL Shared Buffers

PostgreSQL 메모리 구조:
┌─────────────────────────────────────────────┐
│  Shared Memory (전체 프로세스 공유)          │
│  ┌────────────────────────────────────────┐  │
│  │   Shared Buffers (기본 128MB)          │  │
│  │   ← 전체 RAM의 25% 권장               │  │
│  └────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────┐  │
│  │   WAL Buffers                          │  │
│  └────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────┐  │
│  │   Lock Table, Proc Array               │  │
│  └────────────────────────────────────────┘  │
├─────────────────────────────────────────────┤
│  Per-Process Memory (프로세스별)             │
│  ┌────────────────────────────────────────┐  │
│  │   work_mem (정렬/해시 조인용)          │  │
│  │   maintenance_work_mem (VACUUM 등)     │  │
│  └────────────────────────────────────────┘  │
├─────────────────────────────────────────────┤
│  OS Page Cache (나머지 RAM)                  │
│  ← PostgreSQL은 OS 캐시를 적극 활용함       │
└─────────────────────────────────────────────┘

Oracle SGA / PGA

Oracle 메모리 구조:
┌──────────────────────────────────────────────────┐
│  SGA (System Global Area) — 인스턴스 전체 공유   │
│  ┌──────────────────────────────────────────────┐ │
│  │  Database Buffer Cache (데이터 블록 캐시)    │ │
│  ├──────────────────────────────────────────────┤ │
│  │  Shared Pool                                 │ │
│  │  ├── Library Cache (파싱된 SQL, PL/SQL)      │ │
│  │  └── Data Dictionary Cache (Row Cache)       │ │
│  ├──────────────────────────────────────────────┤ │
│  │  Redo Log Buffer                             │ │
│  ├──────────────────────────────────────────────┤ │
│  │  Large Pool (병렬 쿼리, RMAN 등)             │ │
│  ├──────────────────────────────────────────────┤ │
│  │  Java Pool, Streams Pool                     │ │
│  └──────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────┤
│  PGA (Program Global Area) — 서버 프로세스별     │
│  ┌──────────────────────────────────────────────┐ │
│  │  Sort Area, Hash Area                        │ │
│  │  Session Info, Cursor State                  │ │
│  └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

3. MVCC 구현 차이

MVCC(Multi-Version Concurrency Control)는 읽기와 쓰기가 서로를 차단하지 않도록 여러 버전의 데이터를 유지하는 방식이다.

3.1 MySQL/InnoDB — Undo Log 기반

InnoDB의 MVCC는 Undo Log를 참조하는 방식이다. 실제 테이블 페이지에는 최신 버전만 저장되고, 이전 버전은 Undo Log에 보관된다.

InnoDB MVCC 동작 원리:

테이블 페이지 (최신 데이터):
┌──────────────────────────────────────────┐
│ Row: id=1, name='Kim', DB_TRX_ID=100    │
│      DB_ROLL_PTR ──────────────────────►│──┐
└──────────────────────────────────────────┘  │
                                              │
Undo Log Segment:                            │
┌──────────────────────────────────────────┐ │
│ TRX_ID=100 이전 버전: name='Lee'  ◄──────┘ │
│ DB_ROLL_PTR ───────────────────────────►│──┐
└──────────────────────────────────────────┘  │
┌──────────────────────────────────────────┐  │
│ TRX_ID=90 이전 버전: name='Park'  ◄──────┘  │
└──────────────────────────────────────────┘

Read View (트랜잭션 시작 시 생성):
- 현재 활성 트랜잭션 목록 스냅샷
- 자신의 트랜잭션 ID보다 큰 TRX_ID는 무시
- Undo Chain을 역방향으로 따라가며 가시적 버전 탐색

장점:

  • 최신 데이터가 테이블 페이지에 있어 현재 읽기 성능이 좋음
  • Undo Log 파일 관리가 명확함

단점:

  • 오래된 트랜잭션이 살아 있으면 Undo Log가 무한정 증가 가능
  • Long-running transaction은 심각한 성능 저하 유발

3.2 PostgreSQL — Tuple Versioning

PostgreSQL은 Undo Log 없이 테이블 힙 자체에 모든 버전을 저장한다. 각 튜플(행)에 xmin/xmax 트랜잭션 ID를 기록하여 가시성을 판별한다.

PostgreSQL Tuple Versioning:

테이블 힙 페이지 (여러 버전 공존):
┌─────────────────────────────────────────────────────────┐
│ Page Header                                              │
├──────────┬──────────────────────────────────────────────┤
│ ItemID 1 │ Tuple: xmin=90, xmax=100, name='Park'        │
│          │ (TRX 100이 삭제/갱신 → 더 이상 유효하지 않음) │
├──────────┼──────────────────────────────────────────────┤
│ ItemID 2 │ Tuple: xmin=100, xmax=0(살아있음), name='Kim'│
│          │ (현재 유효한 버전)                            │
└──────────┴──────────────────────────────────────────────┘

가시성 규칙:
- xmin < 내 Snapshot XID AND xmax = 0  → 보임 (현재 유효)
- xmin < 내 Snapshot XID AND xmax > 내 Snapshot → 보임 (삭제됐지만 내 기준엔 유효)
- xmin >= 내 Snapshot XID             → 안 보임 (내 이후 삽입)

VACUUM의 역할:

오래된 버전 튜플(Dead Tuple)은 VACUUM이 회수할 때까지 디스크에 남는다. 이로 인해 테이블 파일이 비대해지는 “Table Bloat” 문제가 발생할 수 있다.

VACUUM 동작:
1. Dead Tuple 스캔 (xmax가 완료된 트랜잭션 ID인 튜플)
2. Visibility Map 업데이트
3. Free Space Map 업데이트
4. 필요 시 OS에 공간 반환 (VACUUM FULL만 실제 축소)

autovacuum 파라미터:
autovacuum_vacuum_scale_factor = 0.2  (테이블의 20% 변경 시 트리거)
autovacuum_analyze_scale_factor = 0.1

장점:

  • Undo Log 별도 관리 불필요
  • 충돌 없는 높은 동시성

단점:

  • VACUUM 부하, Table Bloat 위험
  • WAL 볼륨 증가 (구버전 힙이 WAL에도 기록됨)

3.3 Oracle — Undo Tablespace 기반

Oracle은 별도의 Undo Tablespace에 이전 버전 데이터를 저장한다. 구조적으로는 InnoDB의 Undo Log와 유사하지만 Tablespace 단위로 관리되어 더 체계적이다.

Oracle MVCC 구조:

Data Block (현재 버전):
┌─────────────────────────────────────────┐
│ Row: id=1, name='Kim'                   │
│ ITL (Interested Transaction List) Entry │
│ ├── XID: 0x0001.0002.00000003           │
│ └── UBA: Undo Block Address ──────────►│──┐
└─────────────────────────────────────────┘  │
                                             │
Undo Tablespace:                            │
┌─────────────────────────────────────────┐  │
│ Undo Block                              │◄─┘
│ ├── 이전 값: name='Lee'                 │
│ └── 이전 UBA (더 오래된 버전으로의 링크)│
└─────────────────────────────────────────┘

Consistent Read: Oracle에서 SELECT는 SCN(System Change Number) 기반으로 일관성을 보장한다. 쿼리 시작 시의 SCN을 기준으로, 그보다 늦게 커밋된 변경은 Undo에서 되돌려서 읽는다.

3.4 MVCC 방식 종합 비교

항목 MySQL/InnoDB PostgreSQL Oracle
이전 버전 저장 위치 Undo Log Segment 테이블 힙 내부 Undo Tablespace
현재 버전 위치 테이블 페이지 테이블 힙 테이블 블록
가비지 수집 Purge Thread VACUUM Undo 자동 만료(UNDO_RETENTION)
Table Bloat 위험 낮음 높음 (VACUUM 필요) 낮음
Long-running 트랜잭션 영향 Undo 증가 Dead Tuple 누적 Undo 부족 오류 가능
버전 이력 관리 자동 자동 (VACUUM 주기적) UNDO_RETENTION 설정

4. 인덱스 차이

4.1 MySQL / InnoDB 인덱스

InnoDB B+Tree 구조 (Clustered Index):

         Root Node
        ┌──────────┐
        │ 10 │ 50  │
        └──┬──┬────┘
       ┌───┘  └──────┐
   Internal Node   Internal Node
  ┌─────────┐      ┌─────────┐
  │  5 │ 8  │      │ 30│ 40  │
  └──┬──┬───┘      └──┬──┬───┘
     │  └──┐          │  └──┐
  Leaf  Leaf       Leaf   Leaf
  ┌────┐ ┌────┐   ┌────┐  ┌────┐
  │행1 │ │행2 │   │행3 │  │행4 │  ← 실제 행 데이터 포함
  └────┘ └────┘   └────┘  └────┘
     ↔ Doubly Linked List (범위 스캔 효율적)
  • Clustered Index: PK 순으로 물리적 정렬 → PK 범위 스캔 매우 효율적
  • Secondary Index: (Secondary Key, PK) 저장 → 조회 시 PK로 Clustered Index 재탐색(Double Lookup)
  • Covering Index: Secondary Index에 필요한 컬럼 모두 포함 시 Double Lookup 회피 가능
-- Covering Index 예시
CREATE INDEX idx_name_age ON users(name, age);
-- name, age만 SELECT하면 클러스터드 인덱스 재탐색 없이 처리
SELECT name, age FROM users WHERE name = 'Kim';

지원 인덱스 타입:

타입 엔진 용도
B+Tree InnoDB, MyISAM 기본, 범위/동등 검색
Hash Memory 동등 검색만 가능, 범위 불가
Full-Text InnoDB, MyISAM 자연어 전문 검색
Spatial (R-Tree) InnoDB, MyISAM 지리정보 검색

4.2 PostgreSQL 인덱스

PostgreSQL은 가장 다양한 인덱스 타입을 지원하며, 사용자 정의 인덱스 타입도 추가 가능하다.

PostgreSQL 인덱스 타입:

B-Tree (기본):
├── 동등, 범위, 정렬 지원
└── 모든 데이터 타입에 적용 가능

Hash:
├── 동등 검색 특화 (= 연산자만)
└── WAL 로깅 (9.x까지는 크래시 복구 불안정)

GiST (Generalized Search Tree):
├── PostGIS 지리 데이터, 전문 검색
├── 범위 타입, IP 주소, 기하 도형
└── 사용자 정의 데이터 타입 지원 가능

SP-GiST (Space-Partitioned GiST):
├── Quad-Tree, K-D Tree 구현
└── 비균등 분포 데이터에 적합

GIN (Generalized Inverted Index):
├── 배열, JSONB, 전문 검색(tsvector)
├── 복합 값(composite) 빠른 검색
└── 빌드 느림, 검색 빠름

BRIN (Block Range INdex):
├── 물리적으로 정렬된 대용량 테이블
├── 타임스탬프, 시퀀셜 ID 등
├── 매우 작은 인덱스 크기
└── 시계열 데이터에 최적

Partial Index (PostgreSQL 강점):

-- NULL이 아닌 활성 사용자만 인덱싱
CREATE INDEX idx_active_users ON users(email)
WHERE is_active = TRUE AND deleted_at IS NULL;

Expression Index:

-- 소문자 변환 결과를 인덱싱
CREATE INDEX idx_lower_email ON users(lower(email));
SELECT * FROM users WHERE lower(email) = 'kim@example.com'; -- 인덱스 사용

4.3 Oracle 인덱스

타입 설명 적합한 경우
B-Tree 기본 인덱스, 고 카디널리티 대부분의 일반 쿼리
Bitmap 낮은 카디널리티 컬럼 DW/OLAP, 성별·상태 코드 등
Function-Based 표현식 결과를 인덱싱 UPPER(name) 검색
Reverse Key B-Tree 키를 뒤집어 저장 RAC 환경의 우측 편향 방지
Composite 다중 컬럼 복합 조건 쿼리
Index-Organized Table (IOT) 테이블 자체가 B-Tree PK 기반 접근이 대부분인 경우

Bitmap Index 예시:

-- Oracle Bitmap Index
CREATE BITMAP INDEX idx_gender ON users(gender);
-- 내부적으로 각 값(M/F)에 대한 비트 벡터 생성
-- AND/OR 연산이 비트 연산으로 처리되어 매우 빠름
-- 단, DML 많은 OLTP에서는 Lock 경합 심함

4.4 인덱스 타입 비교 표

인덱스 타입 MySQL PostgreSQL Oracle MariaDB SQL Server
B-Tree
Hash ✓(Memory) - ✓(Memory) -
Clustered ✓(InnoDB PK) - (별도 설정) IOT ✓(InnoDB PK)
Bitmap - - - ✓(CS)
GIN/GiST - - - -
BRIN - - - -
Spatial ✓(PostGIS)
Full-Text
Function-Based ✓(Generated) ✓(Expression) ✓(Computed)
Partial - - - ✓(Filtered)

5. 트랜잭션 / 격리 수준

5.1 기본 격리 수준

데이터베이스 기본 격리 수준 이유
MySQL/InnoDB REPEATABLE READ Gap Lock으로 Phantom Read 방지
MariaDB REPEATABLE READ MySQL 호환
PostgreSQL READ COMMITTED 일반적인 OLTP 성능/안전성 균형
Oracle READ COMMITTED 오래된 기본값 유지
SQL Server READ COMMITTED 기본; RCSI 활성화 시 성능 향상

5.2 격리 수준별 문제 현상

격리 수준 계층:
                 낮음 ←────────────────────────────────→ 높음
    READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE
         ↓                  ↓                ↓                ↓
    Dirty Read 허용    Dirty Read 없음   Non-Repeatable   Phantom 없음
    Non-Repeatable     Non-Repeatable    Read 없음         완전 직렬화
    Read 허용          Read 허용         Phantom 허용
    Phantom 허용       Phantom 허용

5.3 MySQL Gap Lock

MySQL REPEATABLE READ에서는 Gap Lock을 사용하여 Phantom Read를 방지한다. 이는 특정 범위에 삽입 자체를 막는 잠금이다.

-- 세션 1:
BEGIN;
SELECT * FROM orders WHERE amount BETWEEN 100 AND 200 FOR UPDATE;
-- Gap Lock: amount 100~200 구간에 삽입 불가 잠금

-- 세션 2 (블록됨):
INSERT INTO orders (amount) VALUES (150); -- 대기 (Gap Lock)

Gap Lock 문제:

  • 데드락(Deadlock) 발생 가능성 증가
  • 동시 삽입 성능 저하

해결책: 격리 수준을 READ COMMITTED로 낮추면 Gap Lock이 없어지지만 Phantom Read가 발생할 수 있다.

5.4 PostgreSQL SSI (Serializable Snapshot Isolation)

PostgreSQL은 SERIALIZABLE 격리 수준에서 SSI를 구현한다. 잠금 없이 의존성 그래프를 추적하여 직렬화 이상을 감지하면 한 트랜잭션을 롤백한다.

SSI 동작 예시 (Write Skew 방지):

트랜잭션 A: SELECT sum(balance) FROM accounts; -- 읽기
트랜잭션 B: SELECT sum(balance) FROM accounts; -- 읽기
트랜잭션 A: UPDATE accounts SET balance = balance - 100 WHERE id=1;
트랜잭션 B: UPDATE accounts SET balance = balance - 100 WHERE id=2;
트랜잭션 A: COMMIT;
트랜잭션 B: COMMIT; -- SSI가 의존성 사이클 감지 → 롤백

→ 실제 직렬화 이상이 감지된 경우만 롤백하므로 불필요한 잠금 없음

5.5 SQL Server RCSI

SQL Server는 기본적으로 잠금 기반이지만, Read Committed Snapshot Isolation(RCSI)을 활성화하면 tempdb에 버전을 저장하여 읽기-쓰기 충돌을 없앨 수 있다.

-- RCSI 활성화
ALTER DATABASE MyDB SET READ_COMMITTED_SNAPSHOT ON;

5.6 격리 수준 지원 비교

격리 수준 MySQL PostgreSQL Oracle MariaDB SQL Server
READ UNCOMMITTED ✓(RC로 처리) -
READ COMMITTED
REPEATABLE READ ✓(기본) - ✓(기본)
SERIALIZABLE ✓(Lock 기반) ✓(SSI) ✓(Lock 기반)
Snapshot - ✓(기본 RR 동작) ✓(별도 지원) - ✓(RCSI)

6. 복제 방식

6.1 MySQL 복제

MySQL 복제 아키텍처:

비동기 복제 (기본):
┌──────────┐   Binary Log   ┌────────────┐
│  Source  │──────────────►│  Replica 1 │
│ (Master) │               └────────────┘
│          │──────────────►┌────────────┐
└──────────┘               │  Replica 2 │
  커밋 즉시 반환            └────────────┘
  (복제 완료 대기 안 함)

반동기 복제 (Semi-Sync):
┌──────────┐  Binlog + ACK  ┌────────────┐
│  Source  │◄──────────────│  Replica 1 │
│          │               └────────────┘
└──────────┘
  최소 1개 복제본의 수신 확인 후 커밋 반환

그룹 복제 (Group Replication / InnoDB Cluster):
┌──────────┐  Paxos 기반   ┌────────────┐
│  Node 1  │◄────────────►│   Node 2   │
│ (Primary)│               └────────────┘
└──────────┘                     ▲
      ▲                          │
      └──────────────────────────┘
               │  Node 3 │
   다수결 커밋 + 자동 장애조치 (Multi-Primary 또는 Single-Primary)

MySQL 복제 포맷:

  • STATEMENT: SQL 문 그대로 기록 (비결정적 함수에서 불일치 위험)
  • ROW: 변경된 행 데이터 기록 (안전하지만 볼륨 큼)
  • MIXED: 기본은 STATEMENT, 비결정적인 경우 ROW 자동 전환

6.2 PostgreSQL 복제

PostgreSQL 스트리밍 복제:
┌─────────────┐  WAL Stream  ┌──────────────┐
│   Primary   │─────────────►│  Standby 1   │
│             │              │ (Hot Standby)│
│             │─────────────►└──────────────┘
└─────────────┘              ┌──────────────┐
                             │  Standby 2   │
                             │(읽기 전용 쿼리│
                             │  가능)       │
                             └──────────────┘

논리적 복제 (Logical Replication):
┌─────────────┐  변경 데이터  ┌──────────────┐
│  Publisher  │─────────────►│  Subscriber  │
│ (선택적     │  (행 단위)   │ (다른 스키마 │
│  테이블)    │              │  버전도 가능)│
└─────────────┘              └──────────────┘
용도: 버전 업그레이드, 특정 테이블만 복제, 이기종 DB 연동

동기/비동기 제어:

-- 동기 복제 설정 (Primary 커밋 전 Standby 확인)
synchronous_standby_names = 'standby1'
-- 또는 QUORUM 방식
synchronous_standby_names = 'FIRST 1 (s1, s2, s3)'

6.3 Oracle Data Guard / GoldenGate

Oracle Data Guard:
┌──────────┐  Redo Log     ┌──────────────┐
│ Primary  │──────────────►│  Standby DB  │
│   DB     │               │(Physical or  │
│          │               │  Logical)    │
└──────────┘               └──────────────┘

Physical Standby: Redo Apply (블록 단위 동일)
Logical Standby:  SQL Apply (SQL 변환 후 적용)
Active Data Guard: Standby를 읽기 전용으로 열어 사용 (추가 라이선스)

Oracle GoldenGate (별도 제품):
┌─────────┐  CDC(변경 캡처)  ┌─────────────┐
│ Source  │────────────────►│   Target    │
│(Oracle/ │                 │(Oracle/비-  │
│비-Oracle│                 │Oracle DB)   │
└─────────┘                 └─────────────┘
용도: 이기종 DB 실시간 복제, 마이그레이션, 양방향 복제

6.4 복제 방식 비교

항목 MySQL PostgreSQL Oracle MariaDB SQL Server
기본 복제 방식 Binlog (비동기) WAL 스트리밍 Data Guard Binlog Always On AG
동기 복제 Semi-Sync, Group Replication synchronous_commit Data Guard Sync Semi-Sync Synchronous Commit
논리 복제 Binlog (Row) Logical Replication LogMiner/GoldenGate Transactional Replication
이기종 복제 제한적 pglogical GoldenGate 제한적 SSIS/외부 도구
자동 장애조치 InnoDB Cluster/MHA Patroni/Repmgr Data Guard FSFO Galera Cluster Always On FCI
지연 복제

7. JSON 지원

7.1 MySQL JSON 타입

MySQL 5.7부터 네이티브 JSON 타입을 지원한다. 내부적으로 Binary JSON 포맷으로 저장되어 부분 업데이트가 가능하다.

-- MySQL JSON 사용 예시
CREATE TABLE events (
    id INT PRIMARY KEY,
    data JSON
);

INSERT INTO events VALUES (1, '{"user": "Kim", "action": "login", "ts": 1234567890}');

-- JSON 경로 조회
SELECT data->>'$.user' AS user_name FROM events;
SELECT JSON_EXTRACT(data, '$.action') FROM events;

-- 부분 업데이트 (MySQL 8.0+)
UPDATE events SET data = JSON_SET(data, '$.action', 'logout') WHERE id = 1;

-- JSON 인덱스 (Generated Column 경유)
ALTER TABLE events
    ADD COLUMN user_name VARCHAR(100) GENERATED ALWAYS AS (data->>'$.user') STORED,
    ADD INDEX idx_user (user_name);

MySQL JSON 한계:

  • JSON 컬럼 자체에 직접 인덱스 불가 (Generated Column 우회 필요)
  • CHECK 제약으로 스키마 강제 불가 (저장 시 유효성만 검사)

7.2 PostgreSQL JSONB

PostgreSQL은 json(텍스트 저장)과 jsonb(바이너리 파싱 후 저장) 두 타입을 지원한다. 실무에서는 거의 항상 jsonb를 사용한다.

-- PostgreSQL JSONB 사용 예시
CREATE TABLE events (
    id SERIAL PRIMARY KEY,
    data JSONB
);

INSERT INTO events VALUES (1, '{"user": "Kim", "tags": ["auth", "web"]}');

-- 키 존재 여부
SELECT * FROM events WHERE data ? 'user';

-- 포함 여부 (Contains)
SELECT * FROM events WHERE data @> '{"user": "Kim"}';

-- 배열 원소 포함 여부
SELECT * FROM events WHERE data->'tags' ? 'auth';

-- GIN 인덱스 (JSON 내 모든 키/값 인덱싱)
CREATE INDEX idx_data_gin ON events USING GIN (data);

-- jsonpath 쿼리 (PostgreSQL 12+)
SELECT jsonb_path_query(data, '$.tags[*]') FROM events;

JSONB 고급 기능:

-- JSON Schema 검증 (PostgreSQL 16+)
SELECT jsonb_matches_schema(
    '{"type": "object", "properties": {"user": {"type": "string"}}}',
    '{"user": "Kim"}'
);

-- JSON 집계
SELECT jsonb_agg(data) FROM events;
SELECT jsonb_object_agg(id, data) FROM events;

7.3 JSON 지원 비교

기능 MySQL PostgreSQL Oracle MariaDB SQL Server
네이티브 JSON 타입 ✓(5.7+) ✓(json/jsonb) ✓(21c 네이티브)
바이너리 저장 ✓(jsonb)
JSON 직접 인덱싱 - (Generated Col) ✓(GIN) ✓(JSON Search) - ✓(Computed Col)
JSON 경로 쿼리 JSON_EXTRACT jsonpath JSON_VALUE JSON_EXTRACT JSON_VALUE
JSON Schema 검증 - ✓(16+) - -
JSON 집계 함수
JSON → 관계형 분해 JSON_TABLE json_to_recordset JSON_TABLE JSON_TABLE OPENJSON

8. 확장성 (Extensibility)

8.1 MySQL 확장성

MySQL의 핵심 확장 포인트는 스토리지 엔진 교체 가능성이다.

MySQL 스토리지 엔진 교체:
┌──────────────────────────────────────────────────┐
│  MySQL Server Layer (파서, 옵티마이저, 캐시 등)  │
├──────────────────────────────────────────────────┤
│  Storage Engine API (핸들러 인터페이스)           │
├──────────────┬───────────┬────────────┬──────────┤
│   InnoDB     │  MyISAM   │  Memory    │  NDB     │
│ (트랜잭션)   │ (읽기 속도)│ (인메모리) │(클러스터)│
└──────────────┴───────────┴────────────┴──────────┘
-- 테이블별로 엔진 선택 가능
CREATE TABLE fast_reads (id INT) ENGINE=MyISAM;
CREATE TABLE transactions (id INT) ENGINE=InnoDB;

플러그인 시스템:

  • 인증 플러그인 (LDAP, Kerberos)
  • 감사 플러그인
  • UDF(User Defined Function) — C/C++로 작성

한계: 새로운 데이터 타입, 연산자, 인덱스 방법 추가 불가

8.2 PostgreSQL 확장 시스템

PostgreSQL의 Extension 시스템은 가장 강력하다. SQL만으로 새로운 타입, 연산자, 인덱스 접근법, 함수 언어를 추가할 수 있다.

-- Extension 설치 (예: PostGIS)
CREATE EXTENSION postgis;
CREATE EXTENSION pg_trgm;   -- 유사 문자열 검색
CREATE EXTENSION uuid-ossp; -- UUID 생성
CREATE EXTENSION tablefunc; -- crosstab, connectby
CREATE EXTENSION pg_stat_statements; -- 쿼리 통계

-- 커스텀 데이터 타입 예시
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TABLE person (current_mood mood);

-- 커스텀 연산자
CREATE OPERATOR === (
    leftarg = integer,
    rightarg = integer,
    procedure = int4eq
);

-- 프로시저 언어 확장
CREATE EXTENSION plpython3u; -- Python으로 함수 작성
CREATE EXTENSION plv8;       -- JavaScript(V8)로 함수 작성

-- 커스텀 인덱스 접근 방법 (Access Method)
-- GiST, SP-GiST, GIN 프레임워크로 새 인덱스 타입 구현 가능

주요 Extension 목록:

Extension 기능
PostGIS 지리정보(GIS) 데이터 처리
pg_trgm 트라이그램 기반 유사 문자열 검색
pgvector 벡터 유사도 검색 (AI/ML 임베딩)
TimescaleDB 시계열 데이터 최적화
Citus 분산 PostgreSQL (샤딩)
pg_partman 파티션 자동 관리
pgcrypto 암호화 함수
hstore key-value 저장

8.3 Oracle 확장성

Oracle PL/SQL 생태계:
┌─────────────────────────────────────────────────────┐
│  PL/SQL (Oracle 전용 절차적 SQL 확장)               │
│  ├── Package, Procedure, Function, Trigger           │
│  ├── Object Types (OOP 스타일)                      │
│  ├── Collection Types (Nested Table, VARRAY)         │
│  └── Pipelined Table Function (스트리밍 처리)        │
├─────────────────────────────────────────────────────┤
│  Advanced Queuing (AQ)                               │
│  ├── 데이터베이스 내장 메시지 큐                     │
│  ├── 트랜잭션 보장 메시징                            │
│  └── Kafka 등 외부 시스템 연동                       │
├─────────────────────────────────────────────────────┤
│  Partitioning (별도 옵션)                            │
│  ├── Range, List, Hash, Composite                   │
│  ├── Interval Partitioning (자동 파티션 생성)        │
│  └── Reference Partitioning (FK 기반)               │
├─────────────────────────────────────────────────────┤
│  Oracle Text (전문 검색), Spatial, XML DB           │
└─────────────────────────────────────────────────────┘

9. 성능 특성

9.1 읽기 중심 워크로드

읽기 성능 특성 비교:

단순 PK 조회:
MySQL InnoDB  ████████████████░░░░  매우 빠름 (Clustered Index)
PostgreSQL    ███████████████░░░░░  빠름 (힙 + 인덱스)
Oracle        ████████████████████  매우 빠름 (버퍼 캐시)
SQL Server    ████████████████░░░░  빠름

복잡한 분석 쿼리:
MySQL         ███████████░░░░░░░░░  중간 (집계 함수 제한)
PostgreSQL    █████████████████░░░  강함 (병렬 쿼리, CTE, 윈도우 함수)
Oracle        ████████████████████  최강 (병렬 실행, Result Cache)
SQL Server    ████████████████████  강함 (OLAP, BI 최적화)

PostgreSQL 병렬 쿼리 예시:

-- 병렬 쿼리 설정
SET max_parallel_workers_per_gather = 4;

-- 대용량 집계 자동으로 병렬 처리
SELECT department, AVG(salary), COUNT(*)
FROM employees
GROUP BY department;
-- → Gather Node가 4개 worker 병렬 집계 후 머지

9.2 쓰기 중심 워크로드

쓰기 성능 특성:

단건 INSERT 처리량 (상대적):
MySQL InnoDB  ████████████████████  빠름 (WAL 순차 쓰기)
PostgreSQL    ████████████████░░░░  빠름 (WAL 순차 쓰기)
Oracle        ████████████████████  빠름 (Redo Log + Change Vector)
SQL Server    ████████████████████  빠름

대량 BULK INSERT:
MySQL         ████████████████████  빠름 (LOAD DATA INFILE)
PostgreSQL    ████████████████████  빠름 (COPY)
Oracle        ████████████████████  빠름 (SQL*Loader, Direct-Path)
SQL Server    ████████████████████  빠름 (BULK INSERT, bcp)

성능에 영향을 미치는 주요 요소:

요소 MySQL PostgreSQL Oracle
WAL 동기화 innodb_flush_log_at_trx_commit synchronous_commit log_buffer_size
쓰기 지연 허용 =2 (1초마다 flush) =off (비동기) ASYNC commit
병렬 처리 InnoDB Parallel DDL max_parallel_workers Parallel DML
Bulk Load LOAD DATA INFILE COPY Direct Path Load

9.3 동시성 처리

동시 쓰기 처리 비교 (같은 행 업데이트):

MySQL REPEATABLE READ:
T1: BEGIN; UPDATE row; -- 잠금 획득
T2: BEGIN; UPDATE row; -- 대기 (Row Lock)
T1: COMMIT;
T2: 잠금 획득 후 실행

PostgreSQL READ COMMITTED:
T1: BEGIN; UPDATE row; -- 최신 버전 잠금
T2: BEGIN; UPDATE row; -- T1 대기
T1: COMMIT;
T2: 최신 버전 재읽기 후 실행 (T1 결과 반영)

Oracle READ COMMITTED:
T1: BEGIN; UPDATE row; -- Row-level TX Lock
T2: BEGIN; UPDATE row; -- T1 대기
T1: COMMIT;
T2: 잠금 획득 (변경된 값 기준으로 재실행)

동시 읽기-쓰기 (MVCC 효과):

모든 MVCC 지원 DB(InnoDB, PostgreSQL, Oracle)에서 SELECT는 UPDATE를 기다리지 않는다. 이것이 전통적 잠금 방식(MyISAM) 대비 가장 큰 장점이다.

9.4 대용량 데이터 처리

기능 MySQL PostgreSQL Oracle SQL Server
테이블 파티셔닝 ✓(제한적) ✓(선언적, 강력) ✓(엔터프라이즈)
병렬 쿼리 ✓(8.0+, 제한적) ✓(강력) ✓(최강)
병렬 DDL
컬럼형 스토리지 - ✓(In-Memory Col) ✓(Columnstore)
인메모리 테이블 Memory 엔진 - ✓(In-Memory) ✓(Hekaton)

10. 운영 / 관리

10.1 백업 / 복구 방식

MySQL 백업

MySQL 백업 방법:

1. 논리 백업 (mysqldump):
mysqldump -u root -p --single-transaction --routines --triggers \
    --all-databases > backup.sql
# --single-transaction: InnoDB 일관성 보장
# 단점: 대용량에서 느림, 복구도 느림

2. 물리 백업 (Percona XtraBackup):
xtrabackup --backup --target-dir=/backup/
xtrabackup --prepare --target-dir=/backup/  # 복구 준비
xtrabackup --copy-back --target-dir=/backup/ # 복구
# 핫 백업 (서비스 중단 없음), 빠른 복구

3. Binary Log 기반 PITR (Point-In-Time Recovery):
mysqlbinlog --start-datetime="2026-05-01 12:00:00" \
            --stop-datetime="2026-05-01 13:00:00" \
            binlog.000001 | mysql -u root -p

PostgreSQL 백업

PostgreSQL 백업 방법:

1. pg_dump (논리 백업):
pg_dump -Fc -f backup.dump mydb   # 커스텀 포맷 (압축)
pg_restore -d mydb backup.dump    # 복구

2. pg_basebackup + WAL (물리 백업 + PITR):
# 베이스 백업
pg_basebackup -D /backup/base -Ft -z -Xs -P

# postgresql.conf 설정
archive_mode = on
archive_command = 'cp %p /archive/%f'

# PITR 복구 시 recovery.conf (또는 postgresql.conf):
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2026-05-01 13:00:00'

3. pgBackRest / Barman (엔터프라이즈 백업 솔루션):
pgbackrest --stanza=mydb backup --type=full
pgbackrest --stanza=mydb restore

Oracle 백업

Oracle RMAN (Recovery Manager):
# 전체 백업
rman target /
RMAN> BACKUP DATABASE PLUS ARCHIVELOG;

# 증분 백업
RMAN> BACKUP INCREMENTAL LEVEL 0 DATABASE;
RMAN> BACKUP INCREMENTAL LEVEL 1 DATABASE;

# PITR 복구
RMAN> RECOVER DATABASE UNTIL TIME "TO_DATE('2026-05-01 13:00:00', 'YYYY-MM-DD HH24:MI:SS')";
RMAN> OPEN RESETLOGS;

10.2 모니터링 도구

도구 대상 DB 설명
Performance Schema MySQL/MariaDB 내장 진단 데이터, 쿼리 통계
sys schema MySQL Performance Schema 뷰 모음
pg_stat_statements PostgreSQL 쿼리별 실행 통계 Extension
pg_activity PostgreSQL top 유사 실시간 모니터링
pgBadger PostgreSQL 로그 분석 리포트
AWR (Automatic Workload Repository) Oracle 성능 스냅샷, Top SQL
ASH (Active Session History) Oracle 1초 단위 활성 세션 샘플링
Percona Monitoring and Management (PMM) MySQL/PG 오픈소스 통합 모니터링
Prometheus + postgres_exporter PostgreSQL 메트릭 수집·시각화
Datadog, New Relic 전체 SaaS 통합 모니터링
-- PostgreSQL 핵심 모니터링 쿼리
-- 느린 쿼리 Top 10
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC LIMIT 10;

-- 블로킹 쿼리 확인
SELECT pid, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE wait_event_type = 'Lock';

-- 테이블별 Dead Tuple 현황
SELECT schemaname, relname, n_live_tup, n_dead_tup,
       last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

10.3 업그레이드 편의성

데이터베이스 마이너 업그레이드 메이저 업그레이드 다운타임
MySQL In-place 가능 In-place(5.7→8.0), 복잡 필요 (최소화 가능)
PostgreSQL In-place 가능 pg_upgrade 도구 필요 pg_upgrade는 빠름
Oracle In-place 가능 DBUA(DB Upgrade Assistant) 최소화 기법 다양
MariaDB In-place 가능 mysql_upgrade 실행 필요
SQL Server In-place 가능 In-place 가능 상대적으로 편리

PostgreSQL 무중단 업그레이드 전략 (논리 복제 활용):

1. 신규 버전 서버 준비
2. 논리적 복제(Logical Replication)로 실시간 데이터 동기화
3. 레플리케이션 지연이 0에 가까워지면 트래픽 전환
4. 전환 후 구버전 서버 종료
→ 다운타임 수 초~분 수준

11. 비용

11.1 라이선스 비용

데이터베이스 무료 에디션 유료 에디션 연간 비용(개략)
MySQL Community Edition(GPL) MySQL Enterprise $10,000~/년
PostgreSQL 완전 무료 (PostgreSQL License) - (지원 계약 별도) $0
Oracle Express Edition(XE, 제한적) Standard One/Enterprise $10,000~$50,000+/CPU
MariaDB Community Edition(GPL) MariaDB Enterprise $7,000~/년
SQL Server Developer(비상용)/Express(제한) Standard/Enterprise $1,418~$15,123/코어

Oracle Enterprise 주요 유료 옵션:

옵션 기능 추가 비용
Partitioning 테이블 파티셔닝 $11,500/Named User
Advanced Compression 압축 스토리지 $11,500/Named User
Active Data Guard Standby 읽기 + PITR $11,500/Named User
Real Application Clusters RAC 클러스터링 $23,000/Named User
Diagnostics & Tuning Pack AWR, ASH, SQL Tuning Advisor $11,500/Named User

11.2 클라우드 매니지드 서비스

클라우드 비용 비교 (AWS 기준, db.r6g.large 4vCPU/32GB 예시):

RDS MySQL:           $0.48/h  →  약 $350/월
RDS PostgreSQL:      $0.48/h  →  약 $350/월
RDS Oracle SE2:      $0.94/h  →  약 $680/월
RDS SQL Server SE:   $0.94/h  →  약 $680/월
Aurora MySQL:        $0.48/h  →  약 $350/월 + 스토리지
Aurora PostgreSQL:   $0.48/h  →  약 $350/월 + 스토리지

Aurora의 특징:

  • MySQL/PostgreSQL 호환 API
  • 스토리지가 6개 복사본 자동 분산 (3 AZ)
  • 최대 15개 읽기 전용 복제본
  • 기존 RDS 대비 최대 5배(MySQL), 3배(PostgreSQL) 성능 향상 주장

클라우드 매니지드 서비스 비교:

클라우드 MySQL PostgreSQL Oracle SQL Server
AWS RDS MySQL, Aurora MySQL RDS PG, Aurora PG RDS Oracle RDS SQL Server
GCP Cloud SQL MySQL Cloud SQL PG, AlloyDB - Cloud SQL SQL Server
Azure Azure Database for MySQL Azure Database for PG Oracle on Azure Azure SQL Database

12. 실무 선택 가이드

12.1 선택 기준 플로우차트

데이터베이스 선택 의사결정:

시작
│
├─ 예산이 제한적인가?
│   YES → 오픈소스 고려
│   │      ├─ MySQL 호환성이 필요한가?
│   │      │   YES → MariaDB
│   │      │   NO  → 복잡한 쿼리/분석 필요?
│   │      │           YES → PostgreSQL
│   │      │           NO  → MySQL
│   │
│   NO → 상용 라이선스 OK
│          ├─ Microsoft 생태계(.NET, Azure)?
│          │   YES → SQL Server
│          │   NO  → 엔터프라이즈/금융/규정 준수?
│                      YES → Oracle
│                      NO  → PostgreSQL or MySQL
│
└─ 클라우드 네이티브 우선?
    YES → Aurora (MySQL/PG 호환)
    NO  → 온프레미스 기준 위 선택

12.2 워크로드별 추천

스타트업 / 소규모 서비스

추천: PostgreSQL 또는 MySQL

PostgreSQL 선택 이유:
✓ 완전 무료, 제한 없는 라이선스
✓ 강력한 기능 (JSON, 윈도우 함수, CTE, 확장)
✓ 데이터 정합성 (더 엄격한 SQL 표준 준수)
✓ 필요 시 확장 가능 (PostGIS, pgvector 등)

MySQL 선택 이유:
✓ 더 많은 레퍼런스, 튜토리얼
✓ 단순 CRUD 앱에서 충분
✓ 호스팅 업체 지원 광범위
✓ Aurora MySQL로 마이그레이션 용이

복잡한 쿼리 / 분석 (OLAP 혼합)

추천: PostgreSQL

PostgreSQL 강점:
- 고급 윈도우 함수 (LEAD, LAG, NTILE, PERCENT_RANK)
- CTE(WITH 절) 최적화
- 병렬 쿼리 (Parallel Seq Scan, Hash Join)
- 파티션 프루닝
- 강력한 Full-Text Search
- GIN 인덱스 (배열, JSONB 검색)
- 커스텀 집계 함수

예시 쿼리 (PostgreSQL 강점):
WITH monthly_sales AS (
    SELECT
        date_trunc('month', created_at) AS month,
        SUM(amount) AS total,
        LAG(SUM(amount)) OVER (ORDER BY date_trunc('month', created_at)) AS prev_month
    FROM orders
    GROUP BY 1
)
SELECT month, total,
       ROUND((total - prev_month) / prev_month * 100, 2) AS growth_pct
FROM monthly_sales;

엔터프라이즈 / 금융권

추천: Oracle Database

Oracle 선택 이유:
✓ 수십 년간의 엔터프라이즈 검증
✓ Data Guard를 통한 무중단 고가용성
✓ RAC (Real Application Clusters) 스케일아웃
✓ 정밀한 보안/감사 (Fine-Grained Auditing)
✓ 규정 준수 (SOX, PCI-DSS, HIPAA)
✓ 강력한 PL/SQL 생태계
✓ Oracle 지원 SLA 보장

고려사항:
△ 매우 높은 라이선스 비용
△ Oracle 전문 DBA 인력 필요
△ 벤더 종속(Vendor Lock-in) 위험

.NET / Microsoft 환경

추천: SQL Server

SQL Server 선택 이유:
✓ Azure 통합 (Azure SQL, Azure Synapse)
✓ .NET Entity Framework, ADO.NET 완벽 지원
✓ SSRS, SSAS, SSIS 통합 (BI 스택)
✓ Always On Availability Groups
✓ T-SQL의 강력한 기능
✓ Visual Studio / SQL Server Management Studio

SQL Server 고유 기능:
- Columnstore Index (OLAP 최적화)
- In-Memory OLTP (Hekaton)
- R/Python 인-데이터베이스 실행
- Stretch Database (Azure 오프로딩)

MySQL 호환 + 커뮤니티

추천: MariaDB

MariaDB 선택 이유:
✓ MySQL 5.x 대비 성능 개선 (특히 읽기)
✓ 완전한 GPL 오픈소스 (Oracle 의존성 없음)
✓ Galera Cluster (동기 멀티마스터)
✓ Aria 스토리지 엔진 (MyISAM 개선판)
✓ ColumnStore (분석용 컬럼 스토리지)
✓ 독자적인 기능 선행 도입

MySQL과의 비호환 사항:
△ MySQL 8.0+ 일부 기능 미지원
△ JSON 함수 일부 차이
△ 인증 플러그인 차이

13. 종합 비교 표

13.1 핵심 기능 비교

기능 MySQL 8.0 PostgreSQL 16 Oracle 21c MariaDB 10.11 SQL Server 2022
라이선스 GPL/상용 PostgreSQL(BSD) 상용 GPL 상용
ACID 지원 ✓(InnoDB) ✓(InnoDB)
기본 격리수준 REPEATABLE READ READ COMMITTED READ COMMITTED REPEATABLE READ READ COMMITTED
MVCC Undo Log Tuple Version Undo Tablespace Undo Log MSSQL Versioning
기본 스토리지 InnoDB 힙(Heap) 테이블스페이스 InnoDB/Aria Extent 기반
클러스터드 인덱스 ✓(PK) 별도 설정 IOT ✓(PK)
파티셔닝 ✓(제한) ✓(강력) ✓(옵션 유료)
외래키 ✓(InnoDB) ✓(InnoDB)
CHECK 제약 ✓(8.0.16+)
윈도우 함수 ✓(8.0+) ✓(10.2+)
CTE (WITH) ✓(8.0+) ✓(10.2+)
재귀 CTE ✓(8.0+) ✓(10.2+)
전문 검색 ✓(기본) ✓(tsvector/GIN) ✓(Oracle Text)
JSON 지원 ✓(JSONB+GIN) ✓(21c 네이티브)
XML 지원 ✓(XMLType)
GIS/공간 데이터 ✓(PostGIS 강력) ✓(Oracle Spatial)
병렬 쿼리 ✓(제한) ✓(강력) ✓(최강) ✓(제한)

13.2 고가용성 / 복제 비교

기능 MySQL PostgreSQL Oracle MariaDB SQL Server
스트리밍 복제 Binlog WAL Data Guard Binlog Always On
동기 복제 Semi-Sync/Group sync_commit DG Sync Semi-Sync/Galera Sync Commit
자동 Failover InnoDB Cluster Patroni/Repmgr DG + Observer Galera/MHA Always On FCI
읽기 분산 복제본 Hot Standby Active DG 복제본 Readable Secondary
멀티마스터 Group Replication BDR (3rd party) RAC Galera -
논리 복제 Binlog Row Logical Rep GoldenGate Binlog Row Transactional Rep

13.3 개발 / SQL 표준 준수 비교

기능 MySQL PostgreSQL Oracle MariaDB SQL Server
SQL 표준 준수도 중간 높음 높음 중간 중간
저장 프로시저 ✓(PL/pgSQL) ✓(PL/SQL) ✓(T-SQL)
트리거 ✓(강력) ✓(강력)
사용자 정의 함수 ✓(다국어)
이벤트 스케줄러 pgAgent DBMS_SCHEDULER SQL Agent
연결 풀링 MySQL Router PgBouncer UCP/Connection Pool MaxScale 내장
EXPLAIN 상세도 중간 높음 (EXPLAIN ANALYZE) 높음 (Autotrace) 중간 높음
DDL 트랜잭션 - (자동 커밋) - (자동 커밋) -

13.4 PostgreSQL만의 고유 강점 정리

PostgreSQL 고유/우위 기능:

인덱스:    GIN, GiST, SP-GiST, BRIN, Partial Index, Expression Index
타입:      배열, hstore, 범위타입(range), 복합타입, 도메인
확장:      Extension 시스템 (PostGIS, pgvector, TimescaleDB 등)
SQL:       Table Inheritance, LATERAL 조인, FILTER 절
동시성:    SSI (Serializable Snapshot Isolation)
복제:      논리적 복제 내장 (추가 비용 없음)
언어:      PL/pgSQL, PL/Python, PL/Perl, PL/v8 (JavaScript)
운영:      DDL 트랜잭션 (마이그레이션 롤백 가능)
개방성:    완전 오픈소스, 커스텀 타입/연산자/집계 함수 추가 가능

13.5 운영 복잡도 비교

항목 MySQL PostgreSQL Oracle MariaDB SQL Server
초기 설정 난이도 낮음 낮음-중간 높음 낮음 낮음-중간
튜닝 파라미터 수 중간 중간 매우 많음 중간 많음
DBA 전문성 요구 낮음-중간 중간 높음 낮음-중간 중간-높음
문서/커뮤니티 풍부 풍부 공식 문서 최강 중간 풍부
인력 수급 매우 쉬움 쉬움 전문 DBA 필요 쉬움 중간
클라우드 지원 광범위 광범위 제한적 일부 Azure 중심

정리

RDBMS 선택에서 “최고의 데이터베이스”는 존재하지 않는다. 워크로드, 팀 역량, 예산, 생태계를 종합적으로 고려해야 한다.

  • PostgreSQL: 기능, 표준 준수, 확장성, 비용 모든 면에서 균형 잡힌 선택. 신규 프로젝트라면 우선 고려.
  • MySQL: 단순 웹 앱, 레거시 호환, 광범위한 호스팅 지원 시 유효.
  • Oracle: 고가용성, 엔터프라이즈 지원, 레거시 PL/SQL 자산이 중요한 대기업/금융.
  • MariaDB: MySQL 대체를 원하지만 Oracle 의존성을 피하고 싶을 때.
  • SQL Server: Microsoft 생태계, Azure, .NET 환경에서 가장 자연스러운 선택.

클라우드 시대에는 Aurora(MySQL/PostgreSQL 호환)나 AlloyDB(PostgreSQL 호환)도 충분히 실용적인 대안이다. 스타트업이라면 PostgreSQL + RDS/Cloud SQL로 시작하여 성장에 따라 Aurora나 Citus(분산 PostgreSQL)로 전환하는 경로를 추천한다.

카테고리:

업데이트: