본문으로 건너뛰기
4.5 디스크 레이아웃과 tablespace

4.5 디스크 레이아웃과 tablespace

PostgreSQL의 모든 데이터는 기본적으로 PGDATA 한 디렉토리에 저장된다(1.5 참고). 하지만 운영 환경에서는 테이블·인덱스·WAL을 다른 디스크에 분산하고 싶은 경우가 흔하다 — 빠른 NVMe와 큰 HDD를 함께 쓰거나, WAL을 별도 디스크로 떼서 commit latency를 줄이거나, hot table을 더 빠른 볼륨에 두거나. 이를 위한 메커니즘이 tablespace다.

tablespace란

tablespace는 이름 + 디스크 경로의 쌍입니다. 테이블·인덱스를 만들 때 어떤 tablespace에 둘지 지정하면, 그 객체의 파일이 해당 경로 아래에 들어갑니다.

-- 1. tablespace 생성 (postgres 사용자가 쓰기 권한을 가진 빈 디렉토리 필요)
CREATE TABLESPACE fast_ssd LOCATION '/mnt/nvme0/pgdata';

-- 2. 테이블을 그 tablespace에 생성
CREATE TABLE hot_orders (...) TABLESPACE fast_ssd;

-- 3. 기존 테이블 이동
ALTER TABLE accounts SET TABLESPACE fast_ssd;

이동(ALTER TABLE SET TABLESPACE)은 ACCESS EXCLUSIVE을 잡으므로 대용량 테이블은 운영 시간 외에 실행합니다. PG 12+의 CREATE INDEX CONCURRENTLY ... TABLESPACE는 인덱스를 무중단으로 다른 tablespace에 만들 수 있습니다.

디스크 구조

tablespace를 만들면 PGDATA에는 심볼릭 링크만 들어가고, 실제 데이터는 외부 경로에 저장됩니다.

$PGDATA/
└── pg_tblspc/
    └── 16400 -> /mnt/nvme0/pgdata    # 심볼릭 링크

/mnt/nvme0/pgdata/
└── PG_17_202402051/                  # 메이저 버전 + catalog version
    └── 16384/                        # 데이터베이스 OID
        ├── 16401                     # relfilenode (테이블 파일)
        ├── 16401_fsm
        └── 16401_vm

PG_<ver>_<catversion> 디렉토리가 한 단계 더 들어가는 이유: 메이저 업그레이드(pg_upgrade) 시 옛 클러스터와 새 클러스터가 같은 디스크를 공유해도 충돌하지 않도록 격리하기 위해서.

기본 tablespace 두 개

initdb 직후 클러스터는 두 개의 logical tablespace를 갖습니다.

tablespace위치용도
pg_default$PGDATA/base/사용자 객체 기본
pg_global$PGDATA/global/클러스터 전역 카탈로그 (pg_database, pg_authid 등). 이동 불가

이 둘은 일반 tablespace처럼 다루지만 PGDATA 안에 위치합니다.

분리 패턴

WAL 분리

가장 흔한 패턴입니다. WAL은 순차 쓰기·짧은 latency가 핵심이라 별도 빠른 디스크에 두면 commit이 빨라집니다.

WAL은 tablespace로 분리하지 않고, PGDATA를 초기화할 때 --waldir 옵션 또는 pg_wal/을 심볼릭 링크로 만듭니다.

# initdb 시
initdb -D /var/lib/pgsql/17/data \
       --waldir=/mnt/wal_nvme/17/pg_wal \
       --data-checksums

# 또는 기존 클러스터에서 — 클러스터 중지 후
pg_ctl stop -D /var/lib/pgsql/17/data
mv $PGDATA/pg_wal /mnt/wal_nvme/pg_wal
ln -s /mnt/wal_nvme/pg_wal $PGDATA/pg_wal
pg_ctl start -D /var/lib/pgsql/17/data

Hot 테이블만 NVMe로

큰 테이블 중 hot 일부만 빠른 디스크에.

CREATE TABLESPACE nvme LOCATION '/mnt/nvme/pgdata';
ALTER TABLE orders SET TABLESPACE nvme;
ALTER TABLE order_items SET TABLESPACE nvme;
-- 인덱스도 같이
ALTER INDEX orders_pkey SET TABLESPACE nvme;

데이터·인덱스를 같이 옮겨야 효과적. 인덱스만 빠른 디스크에 두는 패턴은 옛날 HDD 시절의 흔적이지, 요즘은 함께 옮기는 게 보통입니다.

temp 파일 분리

정렬·해시 spill·임시 테이블이 쓰는 임시 파일을 별도 디스크에.

CREATE TABLESPACE temp_ssd LOCATION '/mnt/temp_nvme/pgdata';

temp_tablespaces 파라미터로 활용:

ALTER SYSTEM SET temp_tablespaces = 'temp_ssd';
SELECT pg_reload_conf();

여러 tablespace를 ,로 나열하면 round-robin으로 분산. 대용량 ETL·warehouse 워크로드에 효과적.

비용·트레이드오프

tablespace는 만능 도구가 아닙니다. 분리해서 얻는 이득과 복잡도를 저울질해야 합니다.

이점비용
빠른 디스크에 hot 데이터 집중두 디스크 모두 백업·모니터링·여유 공간 관리 필요
WAL 분리로 commit latency 개선디스크 1개 장애가 클러스터 정지 (WAL 손실은 곧 데이터 손실)
temp 파일 분리로 OLTP 안정tablespace 디렉토리가 사라지면 클러스터 부팅 실패
백업·복제 도구가 잘 지원하는지 확인 필요pgBackRest, Barman은 OK. 일부 자체 스크립트는 tablespace 미지원

백업과의 상호작용

pg_basebackup·pgBackRest·Barman 모두 tablespace를 인식해 같이 백업합니다. pg_dump는 tablespace 정의도 dump에 포함 (단, --no-tablespaces로 빼면 복원 시 무시).

복원 시 같은 tablespace 경로가 있어야 한다 — 다른 호스트로 복원하면 마운트 포인트가 같지 않을 가능성이 높아 tablespace_map을 직접 편집하는 패턴이 흔하다 (Part XI 백업 참고).

tablespace의 객체 확인

-- 정의 목록
SELECT spcname, pg_tablespace_location(oid) AS location
  FROM pg_tablespace;

-- 어느 테이블이 어느 tablespace에 있나
SELECT n.nspname, c.relname, t.spcname AS tablespace
  FROM pg_class c
  JOIN pg_namespace n ON c.relnamespace = n.oid
  LEFT JOIN pg_tablespace t ON c.reltablespace = t.oid
 WHERE c.relkind IN ('r','i')
 ORDER BY tablespace, c.relname
 LIMIT 20;

-- 데이터베이스의 기본 tablespace
SELECT datname, t.spcname
  FROM pg_database d
  JOIN pg_tablespace t ON d.dattablespace = t.oid;

디스크 레이아웃 의사결정 체크리스트

시나리오권장
클라우드 단일 볼륨 (EBS 등)분리할 가치 적음 — 단일 디스크
베어메탈, NVMe + SATA 혼합WAL은 NVMe, 데이터는 SATA + hot table은 NVMe
워크로드가 단순 OLTP분리하지 말고 단일 PGDATA가 운영·백업 가장 간단
대용량 분석 + OLTP 혼재temp_tablespaces로 분석용 임시 분리
메이저 업그레이드 계획pg_upgrade --link가 tablespace를 잘 다루지만 디스크 마운트 경로가 그대로여야
tablespace 디렉토리를 OS 레벨에서 옮기지 말 것. PGDATA의 심볼릭 링크가 바뀐 경로를 가리키지 않으면 클러스터는 부팅 시 ERROR로 멈춥니다. 옮기려면 항상 SQL(ALTER TABLESPACE ... SET (TABLESPACE = …)) 또는 클러스터 중지 후 심볼릭 링크 재작성합니다.

정리

  • tablespace = 이름 + 외부 디렉토리. 테이블·인덱스를 다른 디스크에 둘 수 있게 함
  • 기본 두 개: pg_default(사용자 데이터), pg_global(전역 카탈로그, 이동 불가)
  • WAL 분리는 --waldir 또는 pg_wal 심볼릭 링크
  • temp 파일 분리는 temp_tablespaces 파라미터 + 전용 tablespace
  • 모든 tablespace는 백업·복제·디스크 장애 관리에 포함시켜야 함
  • 클라우드 단일 볼륨 환경에서는 분리할 가치 적음

Part IV 스토리지와 I/O가 끝났습니다. 다음 Part V에서는 SQL이 실제로 어떻게 실행되는지 — 쿼리 처리의 단계를 봅니다.