[PostgreSQL, MySQL] 인덱스 구조, 설계 목적 비교하기!

728x90

 

 

PostgreSQL, MySQL 인덱스 구조 비교

 

PostgreSQL의 인덱스 구조

- `Heap Table`(실제 저장소) + 별도 인덱스 구조

  : 클러스터 인덱스가 없음

  : PK 인덱스, 보조 인덱스 모두 Heap으로 향하는 포인터 `TID` 를 가짐

- 인덱스 리프 노드에는 `TID (Heap Tuple Pointer)` 만 저장 

  : `TID` 크기는 6 바이트로 작고 고정되어 공간 효율이 좋음

- 실제 데이터를 읽으려면 반드시 `Heap Table 페이지`로 이동해야 함

 

B-Tree Index 
├── Key = (writer_id, created_at) 
└── Value = TID (Heap row pointer)

인덱스만 보고 실제 데이터를 읽을 수 없고,

`TID` 가 가리키는 `Heap 위치` (`Heap Page #, Offset 형태`) 로 이동해야 실제 row를 가져올 수 있다.

> `Heap`은 물리적으로 정렬되어 있지 않기 때문에 `랜덤 I/O`가 발생

 

> Heap Table 이란? <

인덱스로 정렬되지 않은 저장 구조로

행이 삽입 순서대로 저장되며, 각 행의 위치는 인덱스가 별도로 기억한다. (PK로도 정렬되지 않음)

따라서 항상 `인덱스`  → `Heap Fetch` 단계를 거친다.

 

InnoDB(MySQL)의 인덱스 구조

- `Clustered Table`(실제 저장소) + 별도 인덱스 구조

  : PK 순서로 정렬되어 있음

- 인덱스 리프 노드에는 PK 값이 저장

  : PK 크기와 비례하여 커지므로 공간 효율이 떨어짐

- PK 인덱스 트리 자체가 실제 데이터 저장소

 

Secondary Index (보조 인덱스) 
├── key: (writer_id, created_at) 
└── value: PK (memo_id) 

Primary Key (클러스터 인덱스) 
├── key: memo_id 
└── value: 실제 row 전체 (writer_id, created_at, content, ...)

보조 인덱스 리프 페이지에서 찾고,

PK 트리로 이동하여 PK 리프에서 실제 row 전체를 가져올 수 있다.

> 보조 인덱스 리프와 PK 리프는 물리적으로 떨어져 있어, 두 번의 접근 `랜덤 I/O`가  발생

 

 

Heap Table vs Clustered Table

구분 Heap Table Clustered Table
저장 구조 무정렬 Heap PK 순서로 정렬된 트리
PK 역할 Heap 포인터를 가진 보조 인덱스 실제 데이터 저장소
인덱스 리프 내용 TID (페이지 + 슬롯) PK + row 데이터
row 접근 인덱스 > Heap Fetch 인덱스 탐색으로 바로 접근
Full Scan Heap 전체 순차 접근 PK 인덱스 순차 접근
정렬 상태 삽입 순서 PK 기준 정렬
대표 DBMS PostgreSQL, Oracle MySQL(InnoDB)

 

PostgreSQL, MySQL 구조가 다른 이유는?

 

PostgreSQL의 Heap Table

- 테이블은 무정렬 저장소, 인덱스는 데이터 접근의 창구

> 쓰기, 동시성, 일관성 중심의 설계를 택함

 

장점

: Insert 빠름 (정렬 불필요)

: 인덱스 재구성 자유롭고, 독립적

: 다양한 인덱스 패턴에 유연

: MVCC(다중 버전 동시성 제어) 구현이 단순

 

단점

: 랜덤 I/O 많음

: dead tuple로 인한 단편화 발생

: PK 정렬 불가

 

> MVCC 구현이 단순하다고? <

Heap Table 은 update 시 기존 row 를 덮지 않고, Heap 에 새로운 위치에 tuple 버전(v2)를 추가한다.

각 tuple 에는 `xmin (생성 트랜잭션)`, `xmax (삭제 트랜잭션)` 정보가 있어

트랜잭션에서 읽을 때, 스냅샷의 유효한 버전만 읽는다.

따라서 한 트랜잭션이 수정 중이어도 다른 트랜잭션은 수정 전 버전을 안전하게 읽을 수 있다.

 

InnoDB(MySQL)의 Clustered Table

- PK 순서로 데이터를 저장하면 Range Scan과 순차 접근이 빨라짐

> 읽기, 정렬, 트랜잭션 성능 중심의 설계를 택함

 

장점

: PK Range Scan 매우 빠름

: 읽기 효율적 (인덱스와 row가 인접)

: PK 기반 FK 조인 효율적

: 커버링 인덱스 자동화 (PK가 항상 row에 포함)

 

단점 

: 중간 삽입 시 페이지 split 발생 (정렬이 깨지므로)

: PK 변경 시 재정렬 필요

: 모든 보조 인덱스가 PK를 포함하여 필요 공간이 증가

: 비 PK 칼럼 기준 접근 시 lookup 필요

 

> 덮어쓰기가 발생하면 일관성은 어떻게 보장하지? <

Clustered Table 은 row 가 PK 트리에 저장되어 있어 update 시 기존 row 를 덮어쓴다.

따라서 과거 버전은 Undo Log 에 별도로 기록되며,

rollback, consistency read 시 Undo Log 를 참조해야 해서 구조가 복잡하다.

 

PostgreSQL, MySQL 인덱스 생성 방식의 차이

 

항목 PostgreSQL MySQL
방식 Sequence 객체 기반 테이블 내부 카운터 기반
저장 위치 독립 시퀀스 (pg_class) 테이블 메타데이터 내부
특징 시퀀스가 독립 객체라 병렬에 안전 테이블 단위 카운터로 관리
롤백 시  숫자 소비 (건너 뜀) 값 복구 가능
Insert 효율 Heap 환경에서 경합 줄임 Clustered 구조에서 순차 삽입 최적화
설계 목적 Heap 기반의 동시성, 안정성 PK 순차 저장을 통한 I/O 효율 극대화
 

 

 

 

 
 
 
 
728x90