테이블스페이스
테이블스페이스 (Tablespaces)
테이블스페이스(tablespace)는 데이터베이스 관리 시스템에서 견고하고 널리 채택된 기능이에요. 데이터의 물리적 모델링과 논리적 모델링을 분리함으로써 데이터베이스의 수직 확장성을 향상시키는 강력한 수단을 제공해요. 본질적으로 물리적 데이터베이스 모델링의 한 기법으로, 서로 다른 스토리지의 여러 볼륨에 I/O 작업을 효율적으로 분산시킬 수 있게 해줘요. 이를 통해 디스크 병렬 읽기/쓰기 작업으로 성능을 최적화해요.
데이터베이스 업계의 맥락에서 테이블스페이스는 논리적 데이터베이스 모델링 기법인 테이블 파티셔닝과 결합될 때 특히 전략적인 역할을 해요. 대규모 데이터베이스 관리에 매우 유용하며, 테이블과 인덱스를 분리하거나 임시 작업을 실행하는 등의 작업에도 사용돼요.
PostgreSQL의 테이블스페이스는 2005년(버전 8.0)부터 중추적인 역할을 해왔고, 선언적 파티셔닝은 2017년(버전 10)에 도입됐어요. 결과적으로 테이블스페이스는 지원되는 모든 PostgreSQL 릴리스에 원활하게 통합되어 있어요. PostgreSQL 테이블스페이스 문서에서 인용하면:
테이블스페이스를 사용하면 관리자가 PostgreSQL 설치의 디스크 레이아웃을 제어할 수 있어요. 이것은 적어도 두 가지 방식으로 유용해요.
- 첫째, 클러스터가 초기화된 파티션이나 볼륨의 공간이 부족해 확장할 수 없다면, 다른 파티션에 테이블스페이스를 만들어 시스템을 재구성할 수 있을 때까지 사용할 수 있어요.
- 둘째, 테이블스페이스는 관리자가 데이터베이스 객체의 사용 패턴에 대한 지식을 사용해 성능을 최적화할 수 있게 해줘요.
출처: 문서
본문
선언적 테이블스페이스 (Declarative tablespaces)
CloudNativePG는 선언적 테이블스페이스를 통해 PostgreSQL 테이블스페이스를 지원하며, 두 가지 별개의 수준에서 동작해요:
- 쿠버네티스: PGDATA와 WAL 볼륨이 처리되는 것과 동일하게 영구 볼륨 클레임을 관리해요
- PostgreSQL: PostgreSQL 인스턴스의
TABLESPACE전역 객체를 관리해요
쿠버네티스 생태계의 일부로서, CloudNativePG의 선언적 테이블스페이스는 영구 볼륨 클레임(및 영구 볼륨)을 활용해 구현돼요. 클러스터에 정의된 각 테이블스페이스는 자체 영구 볼륨에 자리해요. CloudNativePG가 PVC 생성을 처리해요. 인스턴스 Pod에 필요한 볼륨을 정규화된 위치에 마운트하고, 프라이머리에서 테이블스페이스를 활성화하기 전에 레플리카가 테이블스페이스를 지원할 준비가 되도록 보장해요.
테이블스페이스는 클러스터 생성 시 설정하거나 나중에 추가할 수 있어요. 단, 요청 시 스토리지를 사용할 수 있어야 해요. 현재는 제거할 수 없어요. 하지만 이 제한은 CloudNativePG의 향후 마이너 또는 패치 버전에서 해결될 거예요.
선언적 테이블스페이스 사용하기
선언적 테이블스페이스 사용은 간단해요. 전체 예시는 cluster-example-with-tablespaces.yaml에서 찾을 수 있어요.
사용하려면 새 Cluster 또는 기존 Cluster 리소스에 새 tablespaces stanza를 사용해요:
spec:
instances: 3
# ...
tablespaces:
- name: tbs1
storage:
size: 1Gi
- name: tbs2
storage:
size: 2Gi
- name: tbs3
storage:
size: 2Gi
각 테이블스페이스는 자체 스토리지 섹션을 가지며, 여기서 생성되는 PVC의 크기와 스토리지 클래스를 구성할 수 있어요. 따라서 관리자는 Storage classes and tablespaces에서 설명한 대로 서로 다른 종류의 워크로드에 서로 다른 스토리지 클래스를 사용하도록 계획할 수 있어요.
CloudNativePG는 고가용성 Postgres 클러스터의 각 인스턴스에 대한 영구 볼륨 클레임을 만들어요. 프로비저닝되면 각 Pod에 마운트해요. 그런 다음 CREATE TABLESPACE 명령으로 tbs1, tbs2, tbs3 테이블스페이스가 프라이머리 PostgreSQL 인스턴스에 생성되도록 보장해요. 이 과정은 빠르며, Postgres에서 다음과 같이 확인할 수 있어요:
app=# SELECT oid, spcname FROM pg_tablespace;
oid | spcname
-------+--------------------
1663 | pg_default
1664 | pg_global
16387 | tbs1
16388 | tbs2
16389 | tbs3
(5 rows)
바로 사용을 시작할 수 있어요:
app=# CREATE TABLE fibonacci(num INTEGER) TABLESPACE tbs1;
CREATE TABLE
클러스터 상태에는 테이블스페이스 섹션이 있어요:
status:
<- snipped ->
tablespacesStatus:
- name: atablespace
state: reconciled
- name: another_tablespace
state: reconciled
- name: tablespacea1
state: reconciled
스토리지 클래스와 테이블스페이스
PGDATA 및 WAL 볼륨과 마찬가지로 테이블스페이스에 서로 다른 스토리지 클래스를 사용할 수 있어요. 이것은 데이터 접근 사용량과 기대에 따라 스토리지의 성능과 비용의 균형을 맞춰 리소스를 최적화하는 편리한 방법이에요.
다음 예시가 이 기능을 설명하는 데 도움을 줘요:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: yardbirds
spec:
instances: 3
storage:
size: 10Gi
walStorage:
size: 10Gi
tablespaces:
- name: current
storage:
size: 100Gi
storageClass: fastest
- name: this_year
storage:
size: 500Gi
storageClass: balanced
yardbirds 클러스터 예시는 3개의 서로 다른 스토리지 클래스를 사용하는 4개의 영구 볼륨 클레임을 요청해요:
- 기본 스토리지 클래스 –
PGDATA와 WAL 볼륨이 사용해요. fastest–current테이블스페이스가 데이터베이스에서 가장 활발하고 까다로운 데이터 집합을 저장하는 데 사용해요.balanced–this_year테이블스페이스가 사용자가 거의 접근하지 않고 성능 기대치가 높지 않은 오래된 데이터 파티션을 저장하는 데 사용해요.
그런 다음 수평 테이블 파티셔닝을 활용해 이번 달의 테이블(예: 2023년 12월 facts)을 current 테이블스페이스에 만들 수 있어요:
CREATE TABLE facts_202312 PARTITION OF facts
FOR VALUES FROM ('2023-12-01') TO ('2024-01-01')
TABLESPACE current;
:::info[Important] 이 예시는 PostgreSQL 선언적 파티셔닝에 익숙하다고 가정해요. :::
테이블스페이스 소유권 (Tablespace ownership)
기본적으로 별도로 지정하지 않으면 테이블스페이스는 .spec.bootstrap.initdb.owner에 정의된 app 애플리케이션 사용자가 소유해요. 자세한 내용은 Bootstrap a new cluster를 참조하세요. 이 기본 동작은 대부분의 마이크로서비스 데이터베이스 사용 사례에서 잘 맞아요.
owner stanza에서 테이블스페이스의 소유자를 설정할 수 있어요. 예를 들어 postgres 사용자 같은 경우, 다음 발췌문처럼요:
# ...
tablespaces:
- name: clapton
owner:
name: postgres
storage:
size: 1Gi
:::info[Important] 테이블스페이스의 소유권을 변경한다면 기존 역할(role)을 사용하고 있는지 확인하세요. 그렇지 않으면 클러스터 상태가 문제를 보고하고 수정될 때까지 테이블스페이스 조정을 멈춰요. 상태와 로그를 모니터링하고 즉시 개입해 문제를 수정하는 것은 여러분의 책임이에요. :::
존재하지 않는 소유자로 테이블스페이스를 정의하면 CloudNativePG는 테이블스페이스를 만들 수 없고 이를 클러스터 상태에 반영해요:
spec:
instances: 3
# ...
tablespaces:
- name: tbs1
storage:
size: 1Gi
- name: tbs2
storage:
size: 2Gi
- name: tbs3
owner:
name: badhombre
storage:
size: 2Gi
status:
<- snipped ->
tablespacesStatus:
- name: tbs1
status: reconciled
- name: tbs2
status: reconciled
- error: 'while creating tablespace tbs3: ERROR: role "badhombre" does
not exist (SQLSTATE 42704)'
name: tbs3
status: pending
백업과 복구 (Backup and recovery)
CloudNativePG는 오브젝트 스토어와 볼륨 스냅샷 둘 다에서 테이블스페이스(및 관련 테이블스페이스 맵)의 백업을 처리해요.
:::warning 기본적으로 백업은 레플리카 노드에서 가져와요. 클러스터에 테이블스페이스를 만든 직후에 가져온 백업은 레플리카에서 테이블스페이스의 불완전한 뷰를 만들 수 있고 따라서 불완전한 백업이 될 수 있어요. 이 지연(lag)은 최대 5분 내에 다음 조정(reconciliation)에서 해결돼요. :::
:::warning 기존 클러스터에서 테이블스페이스를 추가하거나 제거하면 새 베이스 백업을 가져올 때까지 WAL 복구가 실패해요. :::
테이블스페이스가 있는 클러스터에 베이스 백업이 생기면 그로부터 새 클러스터를 복원할 수 있어요. 복구 측면에서는 복구된 데이터베이스의 Cluster 정의가 정확한 테이블스페이스 목록을 포함하도록 보장하는 것이 여러분의 책임이에요.
레플리카 클러스터 (Replica clusters)
레플리카 클러스터는 오리진과 동일한 테이블스페이스 정의를 가져야 해요. 그 이유는 CREATE TABLESPACE 같은 테이블스페이스 관리 명령이 WAL 로깅되며 모든 물리적 복제 클라이언트(스트리밍 또는 WAL shipping을 통한)에 의해 재생되기 때문이에요.
레플리카 클러스터가 같은 이름의 동일한 테이블스페이스 목록을 갖도록 보장하는 것은 여러분의 책임이에요. 스토리지 클래스와 크기는 달라질 수 있어요.
예를 들어:
spec:
# ...
bootstrap:
recovery:
# ... your selected recovery method
tablespaces:
- name: tbs1
storage:
size: 1Gi
- name: tbs2
storage:
size: 2Gi
- name: tbs3
storage:
size: 2Gi
임시 테이블스페이스 (Temporary tablespaces)
PostgreSQL은 CREATE 명령이 테이블스페이스를 명시하지 않을 때 임시 객체(임시 테이블과 임시 테이블의 인덱스)를 만들고, 큰 데이터 집합 정렬 같은 목적으로 임시 파일을 만들기 위해 하나 이상의 임시 테이블스페이스를 정의할 수 있게 해줘요. 임시 테이블스페이스가 지정되지 않으면 PostgreSQL은 데이터베이스의 기본 테이블스페이스를 사용하는데, 현재는 메인 PGDATA 볼륨이에요.
하나 이상의 임시 테이블스페이스를 지정하면 PostgreSQL은 트랜잭션에서 임시 객체를 처음 만들어야 할 때 그중 하나를 무작위로 선택해요. 그런 다음 목록을 순차적으로 반복해요.
임시 테이블스페이스는 백업과 관련해 일반 테이블스페이스처럼 동작해요.
CloudNativePG는 .spec.tablespaces[*].temporary 옵션을 제공해서 테이블스페이스를 temp_tablespaces PostgreSQL 파라미터에 추가할지 여부를 결정하고, 따라서 명시적인 테이블스페이스 할당이 없는 임시 데이터를 저장할 자격을 갖게 해요.
spec:
[...]
tablespaces:
- name: atablespace
storage:
size: 1Gi
temporary: true
이는 초기화 시 생성하거나 나중에 추가할 수 있으며, 추가 시 롤링 업데이트가 필요해요. temporary: true/false 옵션은 temp_tablespaces 옵션의 테이블스페이스 목록에 테이블스페이스 이름을 추가하거나 제거해요. 이 변경은 PostgreSQL 재시작을 필요로 하지 않아요.
임시 테이블스페이스는 일반 테이블스페이스로도 동작할 수 있지만(즉, 임시 작업에 사용하면서 일반 데이터를 호스팅할 수도 있다는 뜻), 두 워크로드를 섞지 않는 것을 권장해요.
자세한 내용은 PostgreSQL의 temp_tablespaces 문서를 참조하세요.
kubectl 플러그인 지원
kubectl status 플러그인에는 테이블스페이스 전용 섹션이 있어서 테이블스페이스 상태, 소유자, 임시 플래그, 오류 등의 편리한 개요를 제공해요:
[...]
Tablespaces status
Tablespace Owner Status Temporary Error
---------- ----- ------ --------- -----
atablespace app reconciled true
another_tablespace app reconciled true
tablespacea1 app reconciled false
Instances status
[...]
위 출력은 verbose 모드에서만 사용할 수 있다는 점을 주의하세요 (kubectl cnpg status -v).
제한 사항 (Limitations)
현재 기존 CloudNativePG 클러스터에서 테이블스페이스를 제거할 수 없어요.