codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›data

PostgreSQL 안쪽

2026-04-28 게시· 2026-05-18 갱신·0회 조회

PostgreSQL 안쪽

PostgreSQL 을 오래 쓸수록 한 번쯤 마주치는 개념들이 있습니다. MVCC · 인덱스 · EXPLAIN · 격리 수준 · VACUUM · 파티셔닝.

1. MVCC

Multi-Version Concurrency Control 은 같은 행의 여러 버전을 동시에 보관해 읽기와 쓰기가 서로를 차단하지 않게 하는 동시성 제어 기법입니다. PostgreSQL 은 이 모델 위에 서 있습니다.

  • 트랜잭션은 시작 시점의 스냅샷에서 데이터를 봅니다.
  • 갱신은 새 행 버전을 만들고 옛 버전에 트랜잭션 ID 로 표시를 남깁니다.
  • 어떤 트랜잭션도 더 이상 보지 않는 옛 버전은 VACUUM 의 정리 대상이 됩니다.

장점은 읽기 잠금 없이 일관된 스냅샷을 제공한다는 점입니다. 한계는 죽은 튜플(dead tuple) 이 쌓이며 발생하는 bloat 입니다.

2. 인덱스 종류

PostgreSQL 은 여러 인덱스 액세스 메서드를 기본 제공합니다.

종류 어울리는 자리
B-tree 동등·범위 비교 (기본값). 거의 모든 등호·부등호 검색.
Hash 동등 비교 한정. WAL 기록은 9.6+ 에서 안정화.
GIN 다중 값 컬럼 (tsvector · jsonb · 배열). 풀텍스트·키 존재 검사.
GiST 범위·공간 (PostGIS), 유사도 검색의 일부.
SP-GiST 공간 분할 (Quad-tree · KD-tree 류).
BRIN 매우 큰 테이블의 자연 정렬된 컬럼 (타임스탬프 등). 작은 인덱스 크기.
BLOOM 다중 컬럼 조합 동등 검사 (extension).

색인 선택은 데이터 분포·쿼리 모양에 좌우됩니다. 잘못된 인덱스는 성능보다 디스크·쓰기 비용을 먼저 늘립니다.

3. EXPLAIN 과 EXPLAIN ANALYZE

EXPLAIN 은 플래너가 만든 실행 계획을 보여주고 EXPLAIN ANALYZE 는 실제 실행 시간·행 수까지 측정합니다.

EXPLAIN ANALYZE
SELECT id, email FROM users WHERE email = 'a@b.com';

읽을 때 자주 보는 노드들.

  • Seq Scan: 테이블 전체 스캔. 작은 테이블·낮은 선택도에서 정상.
  • Index Scan / Index Only Scan: 인덱스를 통한 접근. Index Only 는 visibility map 이 깨끗할 때만 의미가 있습니다.
  • Bitmap Heap Scan: 여러 인덱스 결과를 비트맵으로 합쳐 한 번에 힙을 봅니다.
  • Nested Loop / Hash Join / Merge Join: 조인 알고리즘. 데이터 크기·분포에 따라 플래너가 선택.
  • actual time vs cost: cost 는 추정, actual time 은 실측. 차이가 크면 통계가 낡았을 가능성.

BUFFERS 옵션을 켜면 캐시 적중·디스크 읽기 양이 보입니다.

EXPLAIN (ANALYZE, BUFFERS) SELECT ...;

4. 트랜잭션 격리 수준

SQL 표준은 네 단계를 정의합니다. PostgreSQL 의 동작은 이렇습니다.

수준 PostgreSQL 의 실제 동작
Read Uncommitted Read Committed 와 동일하게 처리 (더티 리드 발생 안 함)
Read Committed 기본값. 각 문장마다 새 스냅샷. 비반복 읽기·팬텀 가능.
Repeatable Read 트랜잭션 시작 시점 스냅샷 고정. SSI 보강으로 일부 팬텀 차단.
Serializable SSI (Serializable Snapshot Isolation, 9.1 도입). 직렬화 가능한 결과 보장. 충돌 시 한쪽 트랜잭션 실패.

수준을 높이면 안전성이 오르지만 충돌·재시도 비용이 늘어납니다. 대부분의 OLTP 는 Read Committed 로 충분합니다. 결제·인벤토리 같은 자리에서는 Serializable 또는 명시적 잠금을 검토합니다.

5. VACUUM 과 autovacuum

VACUUM 은 죽은 튜플을 회수하고 가시성 맵·통계를 갱신합니다. autovacuum 데몬이 임계치에 따라 자동으로 실행합니다.

  • VACUUM (기본): 공간을 free space map 에 돌려줍니다. 디스크는 줄지 않습니다.
  • VACUUM FULL: 테이블을 다시 쓰며 디스크까지 줄입니다. 그동안 ACCESS EXCLUSIVE 락이 걸려 운영 중에는 위험합니다.
  • ANALYZE: 통계 수집. 플래너 정확도에 영향.

bloat 는 죽은 튜플이 회수보다 빨리 쌓일 때 발생합니다. 결과는 디스크 사용량 증가, 인덱스 비대, 캐시 적중률 저하입니다. 대량 갱신·삭제 패턴이 있는 테이블에서 흔합니다. 점검 도구로 pgstattuple extension 이 자주 거론됩니다.

6. 파티셔닝

PostgreSQL 10 부터 선언적 파티셔닝이 들어왔습니다. 한 테이블을 키 기준으로 자식 테이블로 나눠 저장합니다.

CREATE TABLE events (
  id BIGSERIAL,
  occurred_at TIMESTAMPTZ NOT NULL,
  payload JSONB
) PARTITION BY RANGE (occurred_at);

CREATE TABLE events_2026_04 PARTITION OF events
  FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');

장점은 큰 테이블의 인덱스·VACUUM 비용을 파티션 단위로 분산할 수 있다는 점입니다. 한계는 각 파티션이 별도 통계를 가지고, 글로벌 인덱스가 (현재까지) 없어 PK · 유니크는 파티션 키를 포함해야 한다는 점입니다.

7. 분산 SQL · 시계열 전용

샤딩 자동화·분산 강한 일관성을 우선하면 Citus (PostgreSQL extension) · CockroachDB · YugabyteDB 같은 후보가 거론됩니다. 단일 노드 PostgreSQL 의 단순함을 잃는 대신 글로벌 규모를 얻습니다.

timescaledb 는 PostgreSQL 위에 시계열 하이퍼테이블·압축·연속 집계 뷰를 더합니다. InfluxDB 같은 자체 엔진이 별도 자리를 갖지만 "기존 SQL 도구를 그대로 쓰고 싶다" 는 요구에서는 timescaledb 가 자주 선택됩니다.

8. 운영 도구

  • CREATE INDEX CONCURRENTLY — 운영 트래픽이 있는 테이블의 인덱스를 만들 때 락을 줄입니다 (다만 테이블당 하나만 동시에 가능).
  • pg_stat_statements — 쿼리별 누적 실행 시간·횟수·plan 을 봅니다. 튜닝의 입구입니다.
  • pg_stat_activity · pg_locks — 현재 세션·잠금 점검. 데드락 추적.
  • 장기 실행 트랜잭션 — 긴 트랜잭션은 vacuum horizon 을 잡아 두어 bloat 를 가속합니다. 주기적인 점검이 필요합니다.

9. 자주 걸리는 자리

JSONB 인덱스 선택 — 쿼리 모양에 맞춰 GIN(jsonb_path_ops) · 식 인덱스를 골라야 합니다. 무조건 GIN 이 정답은 아닙니다.

SELECT * + 큰 행 — TOAST 컬럼이 끌려 나오며 의외의 비용을 만듭니다.

잘못된 통계 — ANALYZE 가 오래 안 됐거나 샘플이 작으면 플래너가 잘못된 plan 을 고릅니다. default_statistics_target 을 컬럼별로 조정할 수도 있습니다.

SERIALIZABLE 의 재시도 의무 — 충돌이 나면 클라이언트가 트랜잭션을 다시 실행해야 합니다. 라이브러리·코드가 이를 가정해야 합니다.

인덱스 비대 — 같은 행이 자주 갱신되면 HOT 업데이트가 깨져 인덱스 항목이 늘어납니다. 주기적인 REINDEX CONCURRENTLY 를 검토합니다.

하고픈 말

PostgreSQL 안쪽은 한 번에 다 알 필요가 없습니다. EXPLAIN 을 읽을 줄 알면 시작이고, 격리 수준·VACUUM 은 사고가 나야 비로소 의미가 보입니다. 그 사고가 운영에 닿기 전에 먼저 만나는 글이 이 자리입니다.

Next

  • three-layer-cache
  • redis-roles

Postgres MVCC 문서 · Postgres 인덱스 종류 · EXPLAIN 사용법 · 트랜잭션 격리 · Routine Vacuuming 을 참고합니다.

data 카테고리의 다른 글

카테고리 전체 보기 →
  • DB 시드 소스를 코드 트리 안에 두지 않는다
  • Supabase Storage — 파일 업로드와 권한
  • Kafka 실무 — 토픽 설계와 메시지 흐름
  • 여러 PostgreSQL 풀 한 앱에서 관리하기
  • 백업과 복구
  • 이미지 파이프라인