InfoGrab DocsInfoGrab Docs

PostgreSQL에서 테이블 칼럼 순서 지정

요약

GitLab에서는 새 테이블의 칼럼을 공간을 가장 적게 쓰는 순서로 배치하도록 요구합니다. C 구조체와 마찬가지로 테이블이 차지하는 공간은 칼럼 순서에 영향을 받습니다. 첫 칼럼은 4바이트 정수입니다. 행과 행 사이의 공간도 정렬 패딩의 대상입니다.

GitLab에서는 새 테이블의 칼럼을 공간을 가장 적게 쓰는 순서로 배치하도록 요구합니다. 간단한 방법은 타입 크기를 기준으로 내림차순으로 정렬하고 가변 크기 타입(text, varchar, 배열, json, jsonb 등)을 맨 뒤에 두는 것입니다.

C 구조체와 마찬가지로 테이블이 차지하는 공간은 칼럼 순서에 영향을 받습니다. 뒤따르는 칼럼의 타입에 따라 칼럼 크기가 정렬되기 때문입니다. 다음 예를 살펴봅니다.

  • id(integer, 4바이트)
  • name(text, 가변)
  • user_id(integer, 4바이트)

첫 칼럼은 4바이트 정수입니다. 다음은 가변 길이 텍스트입니다. text 데이터 타입은 1워드 정렬을 요구하며, 64비트 플랫폼에서 1워드는 8바이트입니다. 정렬 요구 사항을 충족하기 위해 첫 칼럼 바로 뒤에 0 네 개가 추가되므로, id가 4바이트를 차지하고 이어서 4바이트의 정렬 패딩이 들어간 다음에야 name 이 저장됩니다. 따라서 이 경우 4바이트 정수를 저장하는 데 8바이트를 쓰게 됩니다.

행과 행 사이의 공간도 정렬 패딩의 대상입니다. user_id 칼럼은 4바이트만 차지하며, 64비트 플랫폼에서는 다음 행을 "깨끗한" 워드 경계에서 시작할 수 있도록 정렬 패딩으로 0 네 개가 추가됩니다.

그 결과 각 칼럼의 실제 크기는 가변 길이 데이터와 24바이트 튜플 헤더를 제외하면 8바이트, 가변, 8바이트가 됩니다. 즉 4바이트 정수 두 개를 위해 각 행마다 최소 16바이트가 필요합니다. 테이블의 행이 몇 개 되지 않는다면 문제가 되지 않습니다. 그러나 수백만 행을 저장하기 시작하면 순서를 달리해 공간을 절약할 수 있습니다. 위 예에서 가장 좋은 칼럼 순서는 다음과 같습니다.

  • id(integer, 4바이트)
  • user_id(integer, 4바이트)
  • name(text, 가변)

또는 다음과 같습니다.

  • name(text, 가변)
  • id(integer, 4바이트)
  • user_id(integer, 4바이트)

이 예에서는 id와 user_id 칼럼이 함께 묶이므로 두 값을 저장하는 데 8바이트만 있으면 됩니다. 결과적으로 각 행이 8바이트씩 적은 공간을 사용합니다.

Ruby on Rails 5.1부터 ID의 기본 데이터 타입은 8바이트를 사용하는 bigint 입니다. 여기서는 재배치 시나리오를 더 현실적으로 보여 주기 위해 integer를 사용합니다.

타입 크기#

PostgreSQL 문서에 많은 정보가 있지만, 여기서는 찾아보기 쉽도록 자주 쓰는 타입의 크기를 정리합니다. 여기서 "워드"는 워드 크기를 뜻하며, 32비트 플랫폼에서는 4바이트, 64비트 플랫폼에서는 8바이트입니다.

타입 크기 필요한 정렬
smallint 2바이트 1워드
integer 4바이트 1워드
bigint 8바이트 8바이트
real 4바이트 1워드
double precision 8바이트 8바이트
boolean 1바이트 불필요
text / string 가변, 1바이트 + 데이터 1워드
bytea 가변, 1 또는 4바이트 + 데이터 1워드
timestamp 8바이트 8바이트
timestamptz 8바이트 8바이트
date 4바이트 1워드

"가변" 크기는 실제 크기가 저장되는 값에 따라 달라진다는 뜻입니다. PostgreSQL 이 값을 행에 직접 포함할 수 있다고 판단하면 그렇게 하지만, 값이 매우 크면 데이터를 외부에 저장하고 칼럼에는 1워드 크기의 포인터를 저장합니다. 이 때문에 가변 크기 칼럼은 항상 테이블 맨 뒤에 두어야 합니다.

실제 예시#

events 테이블을 예로 듭니다. 현재 이 테이블의 레이아웃은 다음과 같습니다.

칼럼 타입 크기
id integer 4바이트
target_type character varying 가변
target_id integer 4바이트
title character varying 가변
data text 가변
project_id integer 4바이트
created_at timestamp without time zone 8바이트
updated_at timestamp without time zone 8바이트
action integer 4바이트
author_id integer 4바이트

칼럼을 정렬하기 위해 패딩을 추가하면 칼럼이 다음과 같이 고정 크기 청크로 나뉩니다.

청크 크기 칼럼
8바이트 id
가변 target_type
8바이트 target_id
가변 title
가변 data
8바이트 project_id
8바이트 created_at
8바이트 updated_at
8바이트 action, author_id

즉 가변 크기 데이터와 튜플 헤더를 제외하면 행마다 최소 8 * 6 = 48바이트가 필요합니다.

대신 다음 칼럼 순서를 사용해 이를 최적화할 수 있습니다.

칼럼 타입 크기
created_at timestamp without time zone 8바이트
updated_at timestamp without time zone 8바이트
id integer 4바이트
target_id integer 4바이트
project_id integer 4바이트
action integer 4바이트
author_id integer 4바이트
target_type character varying 가변
title character varying 가변
data text 가변

이렇게 하면 다음과 같은 청크가 만들어집니다.

청크 크기 칼럼
8바이트 created_at
8바이트 updated_at
8바이트 id, target_id
8바이트 project_id, action
8바이트 author_id
가변 target_type
가변 title
가변 data

이 경우 가변 크기 데이터와 24바이트 튜플 헤더를 제외하면 행마다 40바이트만 필요합니다. 8바이트 절약이 크지 않아 보일 수 있지만, events 테이블처럼 큰 테이블에서는 의미가 생깁니다. 예를 들어 8000만 행을 저장할 때 칼럼 몇 개의 순서를 바꾸는 것만으로 최소 610MB의 공간을 절약합니다.

PostgreSQL에서 테이블 칼럼 순서 지정

GitLab v19.4
원문 보기

요약

GitLab에서는 새 테이블의 칼럼을 공간을 가장 적게 쓰는 순서로 배치하도록 요구합니다. C 구조체와 마찬가지로 테이블이 차지하는 공간은 칼럼 순서에 영향을 받습니다. 첫 칼럼은 4바이트 정수입니다. 행과 행 사이의 공간도 정렬 패딩의 대상입니다.

GitLab에서는 새 테이블의 칼럼을 공간을 가장 적게 쓰는 순서로 배치하도록 요구합니다. 간단한 방법은 타입 크기를 기준으로 내림차순으로 정렬하고 가변 크기 타입(text, varchar, 배열, json, jsonb 등)을 맨 뒤에 두는 것입니다.

C 구조체와 마찬가지로 테이블이 차지하는 공간은 칼럼 순서에 영향을 받습니다. 뒤따르는 칼럼의 타입에 따라 칼럼 크기가 정렬되기 때문입니다. 다음 예를 살펴봅니다.

  • id(integer, 4바이트)
  • name(text, 가변)
  • user_id(integer, 4바이트)

첫 칼럼은 4바이트 정수입니다. 다음은 가변 길이 텍스트입니다. text 데이터 타입은 1워드 정렬을 요구하며, 64비트 플랫폼에서 1워드는 8바이트입니다. 정렬 요구 사항을 충족하기 위해 첫 칼럼 바로 뒤에 0 네 개가 추가되므로, id가 4바이트를 차지하고 이어서 4바이트의 정렬 패딩이 들어간 다음에야 name 이 저장됩니다. 따라서 이 경우 4바이트 정수를 저장하는 데 8바이트를 쓰게 됩니다.

행과 행 사이의 공간도 정렬 패딩의 대상입니다. user_id 칼럼은 4바이트만 차지하며, 64비트 플랫폼에서는 다음 행을 "깨끗한" 워드 경계에서 시작할 수 있도록 정렬 패딩으로 0 네 개가 추가됩니다.

그 결과 각 칼럼의 실제 크기는 가변 길이 데이터와 24바이트 튜플 헤더를 제외하면 8바이트, 가변, 8바이트가 됩니다. 즉 4바이트 정수 두 개를 위해 각 행마다 최소 16바이트가 필요합니다. 테이블의 행이 몇 개 되지 않는다면 문제가 되지 않습니다. 그러나 수백만 행을 저장하기 시작하면 순서를 달리해 공간을 절약할 수 있습니다. 위 예에서 가장 좋은 칼럼 순서는 다음과 같습니다.

  • id(integer, 4바이트)
  • user_id(integer, 4바이트)
  • name(text, 가변)

또는 다음과 같습니다.

  • name(text, 가변)
  • id(integer, 4바이트)
  • user_id(integer, 4바이트)

이 예에서는 id와 user_id 칼럼이 함께 묶이므로 두 값을 저장하는 데 8바이트만 있으면 됩니다. 결과적으로 각 행이 8바이트씩 적은 공간을 사용합니다.

Ruby on Rails 5.1부터 ID의 기본 데이터 타입은 8바이트를 사용하는 bigint 입니다. 여기서는 재배치 시나리오를 더 현실적으로 보여 주기 위해 integer를 사용합니다.

타입 크기#

PostgreSQL 문서에 많은 정보가 있지만, 여기서는 찾아보기 쉽도록 자주 쓰는 타입의 크기를 정리합니다. 여기서 "워드"는 워드 크기를 뜻하며, 32비트 플랫폼에서는 4바이트, 64비트 플랫폼에서는 8바이트입니다.

타입 크기 필요한 정렬
smallint 2바이트 1워드
integer 4바이트 1워드
bigint 8바이트 8바이트
real 4바이트 1워드
double precision 8바이트 8바이트
boolean 1바이트 불필요
text / string 가변, 1바이트 + 데이터 1워드
bytea 가변, 1 또는 4바이트 + 데이터 1워드
timestamp 8바이트 8바이트
timestamptz 8바이트 8바이트
date 4바이트 1워드

"가변" 크기는 실제 크기가 저장되는 값에 따라 달라진다는 뜻입니다. PostgreSQL 이 값을 행에 직접 포함할 수 있다고 판단하면 그렇게 하지만, 값이 매우 크면 데이터를 외부에 저장하고 칼럼에는 1워드 크기의 포인터를 저장합니다. 이 때문에 가변 크기 칼럼은 항상 테이블 맨 뒤에 두어야 합니다.

실제 예시#

events 테이블을 예로 듭니다. 현재 이 테이블의 레이아웃은 다음과 같습니다.

칼럼 타입 크기
id integer 4바이트
target_type character varying 가변
target_id integer 4바이트
title character varying 가변
data text 가변
project_id integer 4바이트
created_at timestamp without time zone 8바이트
updated_at timestamp without time zone 8바이트
action integer 4바이트
author_id integer 4바이트

칼럼을 정렬하기 위해 패딩을 추가하면 칼럼이 다음과 같이 고정 크기 청크로 나뉩니다.

청크 크기 칼럼
8바이트 id
가변 target_type
8바이트 target_id
가변 title
가변 data
8바이트 project_id
8바이트 created_at
8바이트 updated_at
8바이트 action, author_id

즉 가변 크기 데이터와 튜플 헤더를 제외하면 행마다 최소 8 * 6 = 48바이트가 필요합니다.

대신 다음 칼럼 순서를 사용해 이를 최적화할 수 있습니다.

칼럼 타입 크기
created_at timestamp without time zone 8바이트
updated_at timestamp without time zone 8바이트
id integer 4바이트
target_id integer 4바이트
project_id integer 4바이트
action integer 4바이트
author_id integer 4바이트
target_type character varying 가변
title character varying 가변
data text 가변

이렇게 하면 다음과 같은 청크가 만들어집니다.

청크 크기 칼럼
8바이트 created_at
8바이트 updated_at
8바이트 id, target_id
8바이트 project_id, action
8바이트 author_id
가변 target_type
가변 title
가변 data

이 경우 가변 크기 데이터와 24바이트 튜플 헤더를 제외하면 행마다 40바이트만 필요합니다. 8바이트 절약이 크지 않아 보일 수 있지만, events 테이블처럼 큰 테이블에서는 의미가 생깁니다. 예를 들어 8000만 행을 저장할 때 칼럼 몇 개의 순서를 바꾸는 것만으로 최소 610MB의 공간을 절약합니다.