top of page

UUIDv7이 PostgreSQL 18에 도입

  • seedware
  • 9월 26일
  • 5분 분량

PostgreSQL 18이 곧 출시될 예정이며, 현재 베타 테스트가 진행 중입니다. 이번 릴리스의 여러 개선 사항 중 하나는 UUIDv7 지원입니다. UUIDv7은 타임스탬프 기반 UUID로, B-Tree 인덱스와 잘 호환됩니다. 이 글에서는 UUID에 대한 전반적인 설명, UUIDv7의 유용성, 그리고 PostgreSQL에서 UUIDv7을 사용하는 방법에 대해 알아보겠습니다.

ree

포스트그레SQL 18

PostgreSQL 18 베타 1이 며칠 전 출시되었습니다 . 이번 릴리스에는 새로운 기능, 개선 사항 및 버그 수정이 포함되어 있습니다. 9월에 고품질 릴리스를 출시할 수 있도록, 평소처럼 커뮤니티 여러분의 많은 참여와 문제 보고를 부탁드립니다.

이번 릴리스의 주요 내용은 다음과 같습니다.

  • 비동기 I/O(io_uring 포함) - seq 스캔 및 진공 작업 시 2~3배 속도 향상

  • 다중 열 B-트리 인덱스의 스킵 스캔 + 더욱 스마트한 OR/IN 최적화

  • 주요 업그레이드 중에 플래너 통계를 유지합니다.

  • UUIDv7 함수

  • 가상으로 생성된 열

  • OAuth 로그인 + md5 사용 중단 경고

  • EXPLAIN ANALYZE는 이제 I/O, CPU, WAL을 표시합니다.

  • 비결정적 대조, 케이스폴딩과 같은 시간적 제약

  • 새로운 와이어 프로토콜 버전: 3.2(2003년 이후 최초!)

가장 흥미로운 기능은 아니지만 uuidv7()(비동기 I/O가 가장 기대되는 기능일 겁니다), 아마도 가장 기대되는 기능일 겁니다. 17 버전에 추가될 예정이었는데, 많은 사용자들이 이 기능이 포함되지 않아 다소 실망했습니다. 저는 이 기능이 너무 기대되어 베타 버전을 직접 사용해 보고 블로그에 글을 쓰기로 했습니다.


UUID란 무엇이고 왜 유용한가요?

UUID는 거래부터 회사까지 다양한 항목의 식별자로 사용되는 128비트 값입니다. 공간과 시간에 관계없이 고유하도록 설계되었으며, 중앙 집중식 서비스에 의존하지 않고도 높은 속도로 효율적으로 생성될 수 있습니다.

SERIAL전통적으로 관계형 데이터베이스는 또는 와 같은 자동 증가 유형을 사용하여 identity고유 식별자를 생성했습니다. 이는 단일 머신에서 효율적으로 수행할 수 있지만(이 경우에도 단점이 있습니다), 확장해야 하는 경우 모든 노드에서 고유한 식별자를 생성하는 방법이 필요합니다. Instagram 팀은 Postgres 데이터베이스를 샤딩하면서 UUID로 마이그레이션하는 과정에 대한 짧은 블로그 글을 작성했습니다 .

UUID는 다음과 같은 몇 가지 일반적인 시나리오에서 데이터베이스의 기본 키로 유용합니다.

  • 분산 데이터베이스에서 고유 ID 생성 :


    많은 분산 데이터베이스가 자동 증가(ID) 열을 지원할 수 있지만, 제한 사항과 성능 문제가 있습니다.

  • 추측 불가능한 공개 식별자 :


    제대로 생성된 UUID는 추측, 예측 또는 시스템 정보 유추에 사용될 수 없습니다. 예를 들어, 고객 식별자로 자동 증가를 사용하는 경우, 공격자는 기존 식별자를 모두 스캔하여 사용하려고 시도할 수 있으며, 다음 식별자를 추측하고 고객 수를 추정할 수 있습니다.

  • 클라이언트가 식별자를 생성하도록 허용 :


    UUID를 사용하면 클라이언트가 서버와 조정하지 않고도 사용할 수 있는 식별자를 생성할 수 있습니다. 이는 서버와의 통신을 최소화하려는 모바일 앱 및 서버리스 환경에서 유용합니다.

이러한 장점으로 인해 UUID는 많은 데이터베이스에서 기본 키로 사용됩니다. 그러나 데이터베이스에서 UUID를 사용하는 데에는 세 가지 문제점이 있습니다.

  • 정렬 : UUID는 값에 따라 의미 있게 정렬할 수 없습니다.

  • 인덱스 지역성 : 인덱스에서 새 UUID가 서로 가깝지 않아 삽입이 무작위 위치에서 수행됩니다. 이는 이 블로그 게시물 의 차트에서 볼 수 있듯이 인덱스 팽창 및 기타 성능 문제를 야기할 수 있습니다 .

  • 크기 : UUID는 128비트 값입니다. 대부분의 개발자는 기본 키에 INT32비트 또는 BIGINT64비트를 사용합니다. 매우 작은 레코드가 많은 테이블의 경우, 이는 상당한 오버헤드가 될 수 있습니다.

다음 섹션에서 설명하겠지만 UUIDv7은 이 3가지 문제 중 2가지를 해결합니다.

디스크 공간이나 네트워크 대역폭이 제한되어 있을 때 UUID 크기가 문제가 될 수 있지만, 최신 CPU는 단일 명령어( CMEQSIMD 명령어의 일부)로 128비트 값을 비교할 수 있으므로 UUID에 대한 데이터베이스 작업이 고도로 최적화되어 있다는 점에 유의해야 합니다. 여기서 핵심은 데이터베이스와 애플리케이션 모두에서 UUID를 문자열 표현이 아닌 이진 표현(적절한 UUID 유형)으로 사용해야 한다는 것입니다.


왜 UUIDv7인가요?

UUID는 2005년 RFC 4122 에서 처음 표준화되었습니다. 이 RFC는 UUID의 5가지 변형을 정의하며, 그중 변형 1과 4가 가장 일반적입니다. 이후 이 사양은 2024년 5월에 발표된 RFC 9562 에서 변형 6~8을 추가하도록 개정되었습니다 (최초 공개 작업 초안은 2020년에 발표되었습니다). RFC 9562와 UUIDv7의 탄생을 축하합니다!

사양 업데이트를 촉진하기 위해 RFC 9562에서는 데이터베이스에서 UUID를 기본 키로 사용하는 일반적인 사용 사례를 설명합니다.

UUID가 인기를 얻은 한 분야는 데이터베이스 키입니다. 하지만 원래 [RFC4122]에서 정의한 UUID 버전 1-5에는 다음과 같은 바람직한 특성이 부족합니다.UUIDv4(5.4절 참조)와 같이 시간 순서가 지정되지 않은 UUID 버전은 데이터베이스 인덱스 지역성이 낮습니다. 즉, 연속적으로 생성되는 새 값은 인덱스에서 서로 가깝지 않습니다. 따라서 이러한 값의 삽입은 무작위 위치에서 수행되어야 합니다. 이로 인해 이러한 작업에 사용되는 일반적인 구조(B-트리 및 그 변형)의 성능에 심각한 악영향을 미칠 수 있습니다.널리 배포된 많은 데이터베이스 애플리케이션과 대규모 애플리케이션 공급업체는 데이터베이스 키로 사용할 더 나은 시간 기반 정렬 가능 고유 식별자를 생성하는 문제를 해결하기 위해 노력해 왔습니다. 이로 인해 지난 10년 이상 동안 여러 구현 사례가 조금씩 다른 방식으로 동일한 문제를 해결해 왔습니다.

RFC는 비표준 UUID의 16가지(!) 가지 구현 방식을 명시하고 있으며, 각 구현 방식마다 장단점이 있습니다. 여기에는 인기 있는 ULID, Twitter Snowflake, Instagram ShardId등이 포함됩니다. 이러한 모든 구현 방식은 새로운 사양을 설계할 때 평가되었습니다.

새로운 RFC는 UUID의 3가지 새로운 변형을 명시하고 있지만, 유일하게 흥미로운 것은 UUIDv7 입니다 . UUIDv6는 하위 호환성을 위해서만 도입되었으며, RFC는 "기존 UUIDv1을 사용하지 않는 시스템은 대신 UUIDv7을 사용해야 한다"고 명시하고 있습니다. UUIDv8은 실험적이고 벤더별 확장을 위한 형식을 제공합니다.

UUIDv7은 정렬 및 인덱스 지역성 문제를 모두 해결합니다. 최상위 48비트로 Unix Epoch 타임스탬프를 사용하고, 나머지 74비트는 임의 값으로 사용합니다(추가 비트는 버전 및 변형에 사용됨). 이를 통해 UUID는 시간 순서에 따라 정렬 가능하고 고유합니다. 또한, 이 표준은 UUID에 밀리초 타임스탬프를 포함하거나 신중하게 시드된 카운터를 포함하여 필요한 경우 1초 이내 정렬을 지원하는 옵션을 제공합니다. 결과적으로 UUIDv7은 데이터베이스의 기본 키로 사용하기에 매우 적합하며, 고유성, 정렬 가능성, 우수한 인덱스 지역성을 보장합니다.


PostgreSQL 18의 UUIDv7

PostgreSQL 18 이전 버전까지는 UUIDv7이 기본적으로 지원되지 않았습니다. 내장 함수 gen_random_uuid()가 UUIDv4를 생성했고, 이 인기 있는 uuid-ossp확장 기능은 추가 UUID 변형을 지원했지만, RFC 4122에 명시된 변형으로 제한되었습니다.

PostgreSQL 18에는 UUIDv7 값을 생성하는 새로운 함수가 추가되었습니다 uuidv7(). PostgreSQL 구현에는 타임스탬프 바로 뒤에 12비트 밀리초 미만의 타임스탬프 분수가 포함됩니다(표준에서 허용하지만 필수는 아님). 이를 통해 동일한 PostgreSQL 세션(동일한 백엔드 프로세스)에서 생성된 모든 UUIDv7 값에 대해 단조성이 보장됩니다.

일관성을 위해 PostgreSQL 18에서는 이름과 일치하도록 uuidv4()별칭을 추가했습니다.gen_random_uuid()

호출하면 uuidv7()타임스탬프가 현재 시간인 새로운 UUIDv7 값이 생성됩니다. 다른 시간에 대한 UUIDv7 값을 생성해야 하는 경우, interval함수에 옵셔널을 전달할 수 있습니다.

UUID에서 타임스탬프와 버전을 추출하는 Postgres의 기존 함수도 UUIDv7을 지원하도록 업데이트되었습니다. 다음은 새 함수 사용 방법의 예입니다.


postgres=# select uuidv7();

uuidv7

--------------------------------------

0196ea4a-6f32-7fd0-a9d9-9c815a0750cd

(1 row)


postgres=# select uuidv7(INTERVAL '1 day');

uuidv7

--------------------------------------

0196ef74-8d09-77b0-a84b-5301262f05ad

(1 row)


postgres=# SELECT uuid_extract_version(uuidv4());

uuid_extract_version

----------------------

4

(1 row)


postgres=# SELECT uuid_extract_version(uuidv7());

uuid_extract_version

----------------------

7

(1 row)


postgres=# SELECT uuid_extract_timestamp(uuidv7());

uuid_extract_timestamp

----------------------------

2025-05-19 20:50:40.381+00

(1 row)


postgres=# SELECT uuid_extract_timestamp(uuidv7(INTERVAL '1 hour'));

uuid_extract_timestamp

----------------------------

2025-05-19 21:50:59.388+00

(1 row)


postgres=# SELECT uuid_extract_timestamp(uuidv7(INTERVAL '-1 day'));

uuid_extract_timestamp

----------------------------

2025-05-18 20:51:15.774+00

(1 row)



테이블에서 기본 키로 사용하는 uuidv7()것은 간단하며, 타임스탬프를 추출하는 기능과 함께 사용하면 UUID를 정렬 가능한 키로 쉽게 사용하고 레코드의 생성 시간을 검사할 수도 있습니다.


CREATE TABLE test (

id uuid DEFAULT uuidv7() PRIMARY KEY,

name text

);


INSERT INTO test (name) VALUES ('foo');

INSERT INTO test (name) VALUES ('bar');

-- this will be sorted to the beginning of the list since we are making it 1h older than the other two

INSERT INTO test (id, name) VALUES (uuidv7(INTERVAL '-1 hour'), 'oldest');


SELECT uuid_extract_timestamp(id), name FROM test ORDER BY id;


uuid_extract_timestamp | name

----------------------------+--------

2025-05-19 19:55:43.87+00 | oldest

2025-05-19 20:55:01.304+00 | foo

2025-05-19 20:55:01.305+00 | bar

(3 rows)




 
 
 

댓글


bottom of page