PostgreSQL

PostgreSQL: TOAST

dewstream 2025. 5. 21. 08:00
728x90

※ PostgreSQL: TOAST.
 
안녕하세요. 듀스트림입니다.
 
오늘의 주제는 PostgreSQL의 스토리지 분리 메커니즘인 토스트에 대한 내용입니다.


1. TOAST

  • PostgreSQL에서 TOAST (The Oversized-Attribute Storage Technique)는 테이블 컬럼에 너무 큰 데이터가 들어올 경우 이를 별도의 저장소로 분리해 저장하는 메커니즘입니다.
  • TOAST는 PostgreSQL의 저장 구조와 퍼포먼스 최적화에 매우 중요한 역할을 합니다.

2. TOAST의 필요성

  1. PostgreSQL의 데이터 페이지는 기본값으로 설치할 경우 8KB입니다.
  2. 테이블의 한 row 전체는 하나의 페이지를 초과할 수 없습니다.
  3. text, bytea, json, xml, jsonb, tsvector, tsquery 등의 데이터 타입은 수십 MB 이상 커질 수 있습니다.

이런 oversized attribute를 저장하기 위해 TOAST가 도입되었습니다.


3. TOAST의 동작 방식

TOAST는 다음과 같은 순서로 동작합니다.

  1. 압축 (Compression)
    • TOAST 가능한 타입(storage EXTENDED)은 우선 zlib을 사용한 pglz 압축 시도.
    • 압축된 결과가 원본보다 작을 경우에만 압축본 사용.
    • 이 압축은 TOAST 테이블로 넘어가기 전 첫 번째 절차입니다.
  2. 외부 저장 (Out-of-line Storage)
    • 압축 후에도 row 전체 크기가 2KB 이상이면, 해당 attribute만 TOAST table이라는 별도 테이블에 저장됩니다.
    • 원 테이블에는 TOAST Pointer만 남게 됩니다.

 
+ 원 테이블에 저장된 TOAST 포인터 크기

  • PostgreSQL의 TOAST 포인터는 varattrib_4b_external 구조체로 정의되어 있으며, 다음과 같은 필드를 가집니다.
typedef struct varatt_external {
    int32  va_rawsize;     // 원본 크기
    int32  va_extsize;     // 압축 후 크기
    Oid    va_valueid;     // TOAST chunk_id
    Oid    va_toastrelid;  // TOAST 테이블 OID
} varatt_external;

// 총 4 + 4 + 4 + 4 = 16바이트
// + varlena 헤더까지 포함되면 보통 20~24바이트 정도의 크기로 포인터가 본문에 저장됩니다.

 

postgres/src/include/access/tupmacs.h at master · postgres/postgres

Mirror of the official PostgreSQL GIT repository. Note that this is just a *mirror* - we don't work with pull requests on github. To contribute, please see https://wiki.postgresql.org/wiki/Subm...

github.com

 

postgres/src/include/postgres.h at master · postgres/postgres

Mirror of the official PostgreSQL GIT repository. Note that this is just a *mirror* - we don't work with pull requests on github. To contribute, please see https://wiki.postgresql.org/wiki/Subm...

github.com


4. TOAST 테이블 구조

TOAST 테이블은 자동 생성되며, pg_toast.pg_toast_<oid> 형태입니다.

필드설명
chunk_id원본 row를 식별할 수 있는 id
chunk_seq조각 순서
chunk_data실제 데이터의 일부 (최대 2KB)
TOAST 테이블은 기본적으로 B-Tree 인덱스를 갖습니다 (pg_toast.pg_toast_<oid>_index).

5. Storage 유형과 TOAST의 관계

PostgreSQL은 컬럼별로 TOAST storage 전략을 설정할 수 있습니다.

Storage 옵션설명
PLAIN압축/TOAST 없음 (기본적으로 작은 데이터에만 사용)
EXTENDED압축 + TOAST 허용 (기본)
EXTERNAL압축 없이 TOAST만
MAINTOAST는 최소화하고 가능한 한 본문에 저장

 
구문 예시

ALTER TABLE <table_name> ALTER COLUMN <col_name> SET STORAGE EXTERNAL;

6. TOAST 관련 파라미터

파라미터설명기본값비고
toast_tuple_targetTOAST 처리를 고려하는 row 크기 임계값 (바이트 단위)2048 bytes내부 하드코딩 상수이며, 사용자 설정 불가
TOAST_MAX_CHUNK_SIZETOAST 테이블에 저장되는 각 청크의 최대 크기약 1996 bytes하드코딩 상수이며, 사용자 설정 불가
bytea_outputbytea 데이터의 출력 형식 설정 (hex 또는 escape)hex사용자 설정 가능 (SET bytea_output TO 'escape'; 등)

7. TOAST 튜닝 시 주의사항

  • Autovacuum이 TOAST 테이블에도 필요합니다.
  • TOAST는 비순차적 접근이 많고, 디스크 I/O 병목이 발생할 수 있습니다.
  • TOAST 테이블에 HOT update는 불가능합니다.
  • jsonb, text, xml 등의 칼럼에 큰 데이터가 자주 갱신되는 경우 TOAST 오버헤드가 큽니다.
    • 이 경우 PLAIN 스토리지 설정이나 LOB 전략으로 구조 조정이 필요할 수 있습니다.

8. TOAST 테이블 조회 방법

-- 특정 테이블의 TOAST 테이블 확인
SELECT reltoastrelid::regclass FROM pg_class WHERE relname = 'table_name';

-- TOAST된 칼럼 확인
SELECT attname, attstorage FROM pg_attribute WHERE attrelid = 'table_name'::regclass;

-- TOAST 테이블 row 수 확인
SELECT count(*) FROM pg_toast.pg_toast_<oid>;

9. 예제

테이블 생성

CREATE TABLE toast_test (id serial PRIMARY KEY, big_text text);

Small Data 삽입 (TOAST 발생 X)

데이터 삽입

INSERT INTO toast_test (big_text) SELECT repeat('a', 1000);  -- 약 1KB

 
원 테이블 컬럼 사이즈, 길이 조회

-- TOAST 조건보다 작기 때문에 TOAST 발생 X
-- pg_column_size() 단위는 byte
-- length()는 실제 데이터 길이

SELECT id, pg_column_size(big_text), length(big_text) FROM toast_test;

-- pg_column_size()는 "현재 세션에서 메모리에 로드된 값" 기준으로 동작하기 때문에,
-- TOAST된 컬럼을 접근하면 PostgreSQL이 자동으로 디토스트(de-toast)하여 본문으로 불러오고,
-- 그 결과의 전체 크기를 반환하게 됩니다.
[그림 1] TOAST 발생 X 컬럼 사이즈, 길이 조회 예시

Big Data 삽입 (TOAST 발생)

데이터 삽입

INSERT INTO toast_test (big_text) SELECT repeat('x', 10000000);

 
 
원 테이블 컬럼 사이즈, 길이 조회

SELECT id, pg_column_size(big_text), length(big_text) FROM toast_test;
[그림 2] TOAST 발생 컬럼 사이즈, 길이 조회 예시

 
TOAST 테이블명 조회

-- 원 테이블의 TOAST 테이블 이름 확인
SELECT reltoastrelid::regclass AS toast_table FROM pg_class WHERE relname = 'toast_test';
[그림 3] 원 테이블의 TOAST 테이블명 조회 예시

 
TOAST 테이블 조회

SELECT * FROM pg_toast.pg_toast_16803;

 
TOAST 테이블의 청크_id와 청크 수 조회

-- chunk_id 별 몇 개의 chunk로 나뉘었는지 조회
SELECT chunk_id, count(*) AS chunks FROM pg_toast.pg_toast_16803 GROUP BY chunk_id ORDER BY chunks DESC;
[그림 4] 청크_id 별 청크 수 조회

오늘은 여기까지~
 

728x90

'PostgreSQL' 카테고리의 다른 글

PostgreSQL: WAL  (0) 2025.05.23
PostgreSQL: reload  (0) 2025.05.22
PostgreSQL: VACUUM  (0) 2025.05.17
PostgreSQL: MVCC  (0) 2025.05.16
PostgreSQL: SLRU(Simple Least Recently Used) 버퍼 캐시  (0) 2025.05.15