Postgres 에서 대규모 데이터베이스를 작업하는 경우 이 이야기가 친숙하게 들릴 것입니다. Postgres 데이터베이스가 계속 증가함에 따라 성능이 저하되기 시작하고 저장 공간, 더 정확하게 말하면 이에 대한 비용에 대해 걱정하기 시작합니다. 여러분은 PostgreSQL을 좋아하지만 갖고 싶은 것이 있습니다. 바로 매우 효과적인 데이터 압축 메커니즘입니다.
PostgreSQL 에는 다음과 같은 압축 메커니즘이 있습니다.
데이터 세트의 크기를 줄일 수 있더라도 TOAST(The Overised Attribute Storage Technique)는 기존의 데이터 압축 메커니즘이 아닙니다. TOAST가 무엇인지 이해하려면 먼저 TOAST에 대한 이야기부터 시작해야 합니다.
Postgres의 저장 단위는 페이지라고 하며 고정된 크기(기본적으로 8kB)를 갖습니다. 고정된 페이지 크기를 사용하면 Postgres에 데이터 관리 단순성, 효율성, 일관성 등 많은 이점이 있지만 단점도 있습니다. 일부 데이터 값이 해당 페이지에 맞지 않을 수 있습니다.
TOAST가 등장하는 곳입니다. TOAST는 PostgreSQL이 페이지에 맞지 않는 Postgres의 값을 효율적으로 저장하고 관리하기 위해 사용하는 자동 메커니즘을 나타냅니다. 이러한 값을 처리하기 위해 Postgres TOAST는 기본적으로 내부 알고리즘을 사용하여 값을 압축합니다. 압축 후에도 값이 여전히 너무 크면 Postgres는 해당 값을 별도의 테이블(TOAST 테이블이라고 함)로 이동하고 원래 테이블에 포인터를 남겨 둡니다.
이 문서의 뒷부분에서 살펴보겠지만, 예를 들어 Postgres에 특정 열의 데이터 압축을 방지하도록 지시하여 실제로 사용자로서 이 전략을 수정할 수 있습니다.
TOAST가 적용될 수 있는 데이터 유형은 주로 표준 PostgreSQL 페이지의 크기 제한을 초과할 가능성이 있는 가변 길이 유형입니다. 반면에, integer
, float
, timestamp
와 같은 고정 길이 데이터 유형은 페이지 내에 편안하게 들어가므로 TOAST의 적용을 받지 않습니다.
TOAST가 적용될 수 있는 데이터 유형의 몇 가지 예는 다음과 같습니다.
json
과 jsonb
큰 text
문자열
varchar
및 varchar(n)
( varchar(n)
에 지정된 길이가 충분히 작은 경우 해당 열의 값은 항상 TOAST 임계값 아래로 유지될 수 있습니다.)
바이너리 데이터를 저장하는 bytea
path
및 polygon
과 같은 기하학적 데이터와 geometry
또는 geography
와 같은 PostGIS 유형
TOAST를 이해하는 것은 페이지 크기 개념뿐만 아니라 또 다른 Postgres 저장소 개념인 튜플과도 관련이 있습니다. 튜플은 PostgreSQL 테이블의 행입니다. 일반적으로 TOAST 메커니즘은 튜플 내의 모든 필드의 총 크기가 약 2kB를 초과하는 경우 시작됩니다.
주의를 기울이셨다면 "잠깐만요. 페이지 크기가 약 8kB인데 왜 오버헤드가 발생하는 걸까요?"라고 궁금해하실 수도 있습니다. PostgreSQL은 단일 페이지에 여러 튜플을 저장할 수 있기를 좋아하기 때문입니다. 튜플이 너무 크면 각 페이지에 맞는 튜플의 수가 적어서 I/O 작업이 증가하고 성능이 저하됩니다.
Postgres는 또한 추가 운영 데이터에 맞게 여유 공간을 유지해야 합니다. 각 페이지는 튜플 데이터뿐만 아니라 항목 식별자, 헤더 및 트랜잭션 정보와 같은 데이터 관리를 위한 추가 정보도 저장합니다.
따라서 튜플에 있는 모든 필드의 결합된 크기가 약 2kB(또는 나중에 살펴보겠지만 TOAST 임계값 매개변수)를 초과하면 PostgreSQL은 데이터가 효율적으로 저장되도록 조치를 취합니다. TOAST는 이를 두 가지 주요 방식으로 처리합니다.
압축. PostgreSQL은 이 문서의 뒷부분에서 다룰 압축 알고리즘을 사용하여 튜플 내의 큰 필드 값을 압축하여 크기를 줄일 수 있습니다. 기본적으로 압축이 충분하여 튜플의 전체 크기를 임계값 아래로 가져오면 데이터는 압축된 형식이기는 하지만 기본 테이블에 유지됩니다.
오프라인 저장. 압축만으로는 큰 필드 값의 크기를 줄이는 데 효과적이지 않은 경우 Postgres는 해당 값을 별도의 TOAST 테이블로 이동합니다. 기본 테이블의 원래 튜플이 더 이상 큰 필드 값을 보유하지 않기 때문에 이 프로세스를 "라인 외부" 스토리지라고 합니다. 대신 TOAST 테이블의 대규모 데이터 위치에 대한 "포인터" 또는 참조가 포함됩니다.
이 기사에서는 내용을 약간 단순화했습니다.
pglz
TOAST는 PostgreSQL에서 큰 값을 압축할 수 있다고 언급했습니다. 그러나 PostgreSQL은 어떤 압축 알고리즘을 사용하고 있으며 얼마나 효과적입니까?
pglz
(PostgreSQL Lempel-Ziv)는 PostgreSQL에서 특별히 TOAST에 맞춰 사용하는 기본 내부 압축 알고리즘입니다.
매우 간단한 용어로 작동하는 방법은 다음과 같습니다.
pglz
반복되는 데이터를 피하려고 합니다. 반복되는 데이터를 보면 동일한 내용을 다시 쓰는 대신 이전에 쓴 위치를 다시 가리킵니다. 이러한 "반복 방지"는 공간을 절약하는 데 도움이 됩니다.
pglz
데이터를 읽을 때 최근 본 데이터 중 일부를 기억합니다. 이 최근 기억을 '슬라이딩 윈도우'라고 합니다.
새로운 데이터가 들어오면 pglz
최근에 이 데이터를 본 적이 있는지(슬라이딩 창 내에서) 확인합니다. 그렇다면 데이터를 반복하는 대신 짧은 참조를 작성합니다.
데이터가 새롭거나 참조를 실제 데이터보다 짧게 만들 만큼 충분히 반복되지 않으면 pglz
그대로 기록합니다.
압축된 데이터를 읽을 때가 되면 pglz
해당 참조를 사용하여 원본 데이터를 가져옵니다. 이 프로세스는 참조된 데이터를 찾아 해당 데이터가 속한 위치에 배치하므로 매우 직접적입니다.
pglz
메모리(슬라이딩 창)를 위한 별도의 저장소가 필요하지 않습니다. 압축하는 동안 이동 중에도 빌드하고 압축을 풀 때에도 동일한 작업을 수행합니다.
이 구현은 TOAST 메커니즘 내에서 압축 효율성과 속도 간의 균형을 제공하도록 설계되었습니다. 압축률 측면에서 pglz
의 효율성은 데이터의 특성에 따라 크게 달라집니다.
예를 들어 반복성이 높은 데이터는 엔트로피가 높은 데이터(예: 무작위 데이터)보다 훨씬 더 잘 압축됩니다. 25~50% 범위의 압축률을 볼 수 있지만 이는 매우 일반적인 추정치입니다. 결과는 데이터의 정확한 특성에 따라 크게 달라질 수 있습니다.
기본적으로 PostgreSQL은 앞에서 설명한 절차(압축이 충분하지 않은 경우 먼저 압축하고 다음으로 라인 외부 저장)에 따라 TOAST 메커니즘을 진행합니다. 그러나 열별로 이 동작을 미세 조정하려는 시나리오가 있을 수 있습니다. PostgreSQL에서는 TOAST 전략 PLAIN
, EXTERNAL
, EXTENDED
및 MAIN
사용하여 이를 수행할 수 있습니다.
EXTENDED
: 기본 전략입니다. 이는 데이터가 일반 테이블 페이지에 비해 너무 큰 경우 별도의 TOAST 테이블에 데이터가 저장되지 않음을 의미합니다. 데이터를 TOAST 테이블로 이동하기 전에 공간 절약을 위해 압축됩니다.
EXTERNAL
: 이 전략은 데이터가 너무 커서 일반 테이블 페이지에 맞지 않는 경우 PostgreSQL에 이 열의 데이터를 라인 외부에 저장하도록 지시하며 우리는 PostgreSQL에 데이터를 압축하지 말라고 요청합니다. 값은 단지 TOAST 테이블을 그대로.
MAIN
: 이 전략은 중간 지점입니다. 압축을 통해 기본 테이블의 데이터를 일렬로 유지하려고 합니다. 데이터가 확실히 너무 크면 오류를 방지하기 위해 데이터를 TOAST 테이블로 이동하지만 PostgreSQL은 압축된 데이터를 이동하지 않습니다. 대신 TOAST 테이블에 값을 원래 형식으로 저장합니다.
PLAIN
: 열에 PLAIN
사용하면 PostgreSQL이 열의 데이터를 항상 기본 테이블의 라인에 저장하여 라인 외부 TOAST 테이블로 이동하지 않도록 합니다. 데이터가 페이지 크기를 초과하면 데이터가 맞지 않기 때문에 INSERT
실패한다는 점을 고려하세요.
특정 테이블의 현재 전략을 검사하려면 다음을 실행할 수 있습니다.
\d+ your_table_name
다음과 같은 출력이 표시됩니다.
=> \d+ example_table Table "public.example_table" Column | Data Type | Modifiers | Storage | Stats target | Description ---------+------------------+-----------+----------+--------------+------------- bar | varchar(100000) | | extended | |
저장소 설정을 수정하려면 다음 명령을 사용하면 됩니다.
-- Sets EXTENDED as the TOAST strategy for bar_column ALTER TABLE example_blob ALTER COLUMN bar_column SET STORAGE EXTENDED;
위의 전략 외에도 다음 두 매개변수도 TOAST 동작을 제어하는 데 중요합니다.
TOAST_TUPLE_THRESHOLD
크기가 큰 튜플에 대해 TOASTing 작업(압축 및 라인 외부 저장)을 고려할 때 크기 임계값을 설정하는 매개변수입니다.
이전에 언급했듯이 기본적으로 TOAST_TUPLE_THRESHOLD
약 2kB로 설정됩니다.
TOAST_COMPRESSION_THRESHOLD
TOASTing 과정에서 Postgres가 압축을 고려하기 전 값의 최소 크기를 지정하는 매개변수입니다.
값이 이 임계값을 초과하면 PostgreSQL은 해당 값을 압축하려고 시도합니다. 그러나 값이 압축 임계값을 초과한다고 해서 자동으로 압축된다는 의미는 아닙니다. TOAST 전략은 압축 여부와 튜플에 상대적인 결과 크기를 기반으로 데이터를 처리하는 방법에 대해 PostgreSQL에 지침을 제공합니다. 다음 섹션에서 살펴보겠지만 페이지 제한입니다.
TOAST_TUPLE_THRESHOLD
는 트리거 포인트입니다. 결합된 튜플의 데이터 필드 크기가 이 임계값을 초과하면 PostgreSQL은 압축 및 라인 외부 저장을 고려하여 해당 열에 대해 설정된 TOAST 전략을 기반으로 이를 관리하는 방법을 평가합니다. 취해지는 정확한 조치는 열 데이터가 TOAST_COMPRESSION_THRESHOLD
를 초과하는지 여부에 따라 달라집니다.
EXTENDED
(기본 전략): 튜플의 크기가 TOAST_TUPLE_THRESHOLD
초과하는 경우 PostgreSQL은 크기가 너무 큰 열이 TOAST_COMPRESSION_THRESHOLD
를 초과하는 경우 먼저 크기가 큰 열을 압축하려고 시도합니다. 압축으로 인해 튜플 크기가 임계값 미만이 되면 기본 테이블에 유지됩니다. 그렇지 않은 경우 데이터는 라인 외부 TOAST 테이블로 이동되고 기본 테이블에는 이 외부 데이터에 대한 포인터가 포함됩니다.
MAIN
: 튜플 크기가 TOAST_TUPLE_THRESHOLD
초과하면 PostgreSQL은 너무 큰 열을 압축하려고 시도합니다( TOAST_COMPRESSION_THRESHOLD
초과하는 경우). 압축을 통해 튜플이 기본 테이블의 튜플에 들어갈 수 있으면 해당 튜플은 그대로 유지됩니다. 그렇지 않은 경우 데이터는 압축되지 않은 형태로 TOAST 테이블로 이동됩니다.
EXTERNAL
: PostgreSQL은 TOAST_COMPRESSION_THRESHOLD
에 관계없이 압축을 건너뜁니다. 튜플의 크기가 TOAST_TUPLE_THRESHOLD
초과하는 경우 크기가 너무 큰 열은 TOAST 테이블의 라인 외부에 저장됩니다.
PLAIN
: 데이터가 항상 메인 테이블에 저장됩니다. 튜플의 크기가 페이지 크기를 초과하면(열이 매우 크기 때문에) 오류가 발생합니다.
전략 | 튜플 > TOAST_COMPRESSION_THRESHOLD인 경우 압축 | 튜플 > TOAST_TUPLE_THRESHOLD인 경우 라인 외부에 저장 | 설명 |
---|---|---|---|
펼친 | 예 | 예 | 기본 전략. 먼저 압축한 다음 라인 외부 저장소가 필요한지 확인합니다. |
기본 | 예 | 압축되지 않은 형태로만 가능 | 먼저 압축하고, 여전히 너무 큰 경우 압축하지 않고 TOAST 테이블로 이동합니다. |
외부 | 아니요 | 예 | 크기가 너무 큰 경우 압축 없이 항상 TOAST로 이동합니다. |
솔직한 | 아니요 | 아니요 | 데이터는 항상 기본 테이블에 유지됩니다. 튜플이 페이지 크기를 초과하면 오류가 발생합니다. |
이제 TOAST가 PostgreSQL에 있었으면 하는 데이터 압축 메커니즘이 아닌 이유를 이해하게 될 것입니다. 최신 애플리케이션은 매일 수집되는 대량의 데이터를 의미하며, 이는 데이터베이스가 (과도하게) 빠르게 성장한다는 것을 의미합니다.
우리가 사랑하는 Postgres가 수십 년 전에 구축되었을 때는 이러한 문제가 눈에 띄지 않았지만 오늘날의 개발자에게는 데이터 세트의 저장 공간을 줄이기 위한 압축 솔루션이 필요합니다.
TOAST는 압축을 기술 중 하나로 통합하지만, 주요 역할이 전통적인 의미의 데이터베이스 압축 메커니즘 역할을 하는 것이 아니라는 점을 이해하는 것이 중요합니다. TOAST는 주로 Postgres 페이지의 구조적 범위 내에서 큰 값을 관리하는 문제에 대한 솔루션입니다.
이 접근 방식은 특정 큰 값의 압축으로 인해 일부 저장 공간을 절약할 수 있지만, 이 방법의 주요 목적은 전체 저장 공간을 최적화하는 것이 아닙니다.
예를 들어, 작은 튜플로 구성된 5TB 데이터베이스가 있는 경우 TOAST는 해당 5TB를 1TB로 변환하는 데 도움이 되지 않습니다. TOAST에는 조정 가능한 매개변수가 있지만, 이로 인해 TOAST가 일반화된 저장 공간 절약 솔루션으로 변환되지는 않습니다.
그리고 PostgreSQL의 전통적인 압축 메커니즘으로 TOAST를 사용하는 데에는 다른 고유한 문제가 있습니다. 예를 들면 다음과 같습니다.
TOAST된 데이터에 액세스하면 오버헤드가 추가될 수 있으며, 특히 데이터가 라인 외부에 저장되어 있는 경우 더욱 그렇습니다. 이는 많은 대용량 텍스트 또는 기타 TOAST 가능 데이터 유형에 자주 액세스할 때 더욱 분명해집니다.
TOAST에는 압축 정책을 지시하는 사용자 친화적인 높은 수준의 메커니즘이 부족합니다. 스토리지 비용을 최적화하거나 스토리지 관리를 용이하게 하기 위해 구축되지 않았습니다.
TOAST의 압축은 특별히 높은 압축률을 제공하도록 설계되지 않았습니다. 압축률이 일반적으로 25~50%인 하나의 알고리즘( pglz
)만 사용합니다.
대규모 테이블에 압축 정책을 추가하면
시간 기반 압축 정책을 정의하여 데이터를 압축해야 하는 시기를 나타냅니다. 예를 들어, 7일이 지난 데이터를 자동으로 압축하도록 선택할 수 있습니다.
-- Compress data older than 7 days SELECT add_compression_policy('my_hypertable', INTERVAL '7 days');
이 압축 정책을 통해 Timescale은 테이블을 변환합니다.
플로트용 고릴라 압축
몇 가지 반복 값이 있는 열에 대한 전체 행 사전 압축(+ 맨 위에 LZ 압축)
다른 모든 유형에 대한 LZ 기반 배열 압축
이 컬럼 압축 설계는 PostgreSQL의 대규모 데이터 세트 문제에 대한 효율적이고 확장 가능한 솔루션을 제공합니다. 이를 통해 쿼리 성능을 저하시키지 않고 더 적은 스토리지를 사용하여 더 많은 데이터를 저장할 수 있습니다(향상됩니다). 그리고 최신 버전의 TimescaleDB에서는 압축된 데이터에 대해 직접 INSERT
, DELETE
및 UPDATE
수행할 수도 있습니다.
이 기사가 TOAST가 PostgreSQL 페이지 내에서 큰 값을 관리하기 위해 세심하게 고안된 메커니즘이지만 최신 애플리케이션 영역 내에서 데이터베이스 스토리지 사용을 최적화하는 데는 효과적이지 않다는 점을 이해하는 데 도움이 되었기를 바랍니다.
스토리지 절감 효과를 높일 수 있는 효과적인 데이터 압축을 찾고 있다면 Timescale을 사용해 보세요. PostgreSQL을 새로운 성능 수준으로 끌어올려 더 빠르고 강력하게 만드는 클라우드 플랫폼을 사용해 볼 수 있습니다.
카를로타 소토가 각본을 맡았습니다.