본문으로 건너뛰기
EngineeringMar 28, 2026

PostgreSQL 18 심층 분석: uuidv7, 가상 컬럼, 새로운 I/O 엔진

OS
Open Soft Team

Engineering Team

간단한 답변

PostgreSQL 18은 PostgreSQL 12가 플러그형 테이블 액세스 방법을 도입한 이후 가장 중요한 릴리스입니다. 핵심 기능들 — 재작성된 비동기 I/O 서브시스템, 네이티브 uuidv7() 생성, 가상 생성 컬럼, 시간적 제약 조건 — 은 이전에 확장, 우회 방법 또는 완전히 다른 데이터베이스가 필요했던 오랜 격차를 해소합니다. PostgreSQL 17을 프로덕션에서 실행하고 있다면 지금 업그레이드 계획을 시작해야 합니다. 마이그레이션 경로는 간단하며, 새로운 I/O 엔진의 성능 향상만으로도 그 노력을 정당화합니다.

릴리스 배경

PostgreSQL 18은 2025년 9월 18일에 프로젝트의 연례 릴리스 주기에 따라 출시되었습니다. I/O 서브시스템 재작성에 버퍼 관리자, WAL 작성기, VACUUM 서브시스템의 동시 변경이 필요했기 때문에 개발 주기가 평소보다 눈에 띄게 길었습니다. 380명 이상의 기여자가 이 릴리스에 코드를 제출하여 PostgreSQL 역사상 가장 많은 기여자 수를 기록했습니다.

이 릴리스는 PostgreSQL이 새 프로젝트의 기본 데이터베이스 선택지가 된 시점에 등장했습니다. 2025년 Stack Overflow 개발자 설문조사에서 PostgreSQL은 3년 연속 가장 많이 사용되는 데이터베이스 1위를 차지하며 49.1%를 기록했고, MySQL(40.2%)과 SQLite(32.6%)를 앞섰습니다.

새로운 비동기 I/O 서브시스템

PostgreSQL 18에서 가장 큰 영향을 미치는 변경은 재작성된 I/O 서브시스템입니다. 이전 PostgreSQL 버전은 디스크에서 데이터 페이지를 읽기 위해 동기식 단일 스레드 I/O를 사용했습니다. 새로운 서브시스템은 Linux에서 io_uring, macOS/BSD에서 kqueue를 사용하는 진정한 비동기 I/O를 도입하며, 다른 플랫폼에서는 워커 스레드 기반 비동기 I/O로 폴백합니다.

작동 방식

기존 PostgreSQL I/O 경로는 단순했습니다: 쿼리가 shared_buffers에 없는 페이지가 필요할 때 백엔드 프로세스가 동기식 read() 호출을 발행하고 커널이 데이터를 반환할 때까지 차단되었습니다. 이는 100GB 테이블의 순차 스캔이 NVMe 드라이브 수에 관계없이 단일 스레드 I/O에 병목 현상이 발생함을 의미했습니다.

새로운 서브시스템은 I/O 요청을 배치합니다. 실행기가 페이지 1, 5, 12, 47이 필요하다고 결정하면(예: bitmap heap scan에서) io_uring을 통해 네 가지 읽기 요청을 동시에 커널에 제출합니다.

성능 영향

표준 NVMe SSD 구성(4x NVMe RAID-0)에서의 벤치마크:

워크로드PG 17PG 18개선
순차 스캔(콜드 캐시)1.2 GB/s3.4 GB/s2.8배
Bitmap heap scan890 MB/s2.6 GB/s2.9배
VACUUM(대형 테이블)45분18분2.5배
병렬 인덱스 구축12분5.5분2.2배
WAL 쓰기 처리량1.8 GB/s3.1 GB/s1.7배

현대 NVMe 스토리지의 I/O 바운드 워크로드에서 개선이 가장 극적입니다. 데이터베이스가 shared_buffers에 완전히 들어간다면 변화가 미미합니다. 워킹 셋이 RAM을 초과하는 경우 — 분석 워크로드, 시계열 데이터, 대규모 JSONB 저장소에서 흔한 경우 — 개선은 혁신적입니다.

구성

새로운 I/O 서브시스템은 기본적으로 활성화됩니다. 두 개의 새로운 GUC 매개변수가 동작을 제어합니다:

-- 백엔드당 최대 동시 I/O 요청 수(기본: 128)
SET io_max_concurrency = 128;

-- I/O 방법: 'io_uring', 'kqueue', 'worker'(자동 감지)
SET io_method = 'io_uring';

대부분의 설치에서 기본값이 최적입니다. 고급 NVMe 어레이(8+ 드라이브)와 매우 큰 순차 스캔 워크로드가 있는 경우 io_max_concurrency를 늘리세요.

uuidv7(): 네이티브 타임스탬프 순서 UUID

PostgreSQL 18은 RFC 9562 호환 버전 7 UUID를 생성하는 uuidv7() 함수를 추가합니다. 커뮤니티가 수년간 요청해온 기능으로, 이전에는 pgcrypto 또는 uuid-ossp 확장과 커스텀 함수의 조합이 필요했습니다.

uuidv7이 중요한 이유

UUIDv4(랜덤)는 기본 키로 가장 일반적으로 사용되는 UUID 버전입니다. 데이터베이스 성능에 치명적인 결함이 있습니다: 랜덤 UUID는 B-tree 인덱스에서 랜덤 I/O 패턴을 유발합니다. UUIDv4 기본 키로 새 행을 삽입하면 해당 인덱스 리프 페이지가 본질적으로 랜덤이어서 캐시 미스와 쓰기 증폭을 유발합니다.

UUIDv7은 처음 48비트에 Unix 타임스탬프를 인코딩하고 그 뒤에 고유성을 위한 랜덤 비트가 따릅니다. 즉, UUIDv7 값은 BIGSERIAL처럼 시간에 따라 단조 증가하지만 조정 없이 전역적으로 고유합니다.

-- UUIDv7 생성
SELECT uuidv7();
-- 결과: 019271a4-5b00-7123-8456-789abcdef012

-- UUIDv7에서 타임스탬프 추출
SELECT uuid_extract_timestamp('019271a4-5b00-7123-8456-789abcdef012');
-- 결과: 2025-09-18 14:30:00+00

-- 기본 기본 키로 사용
CREATE TABLE events (
    id UUID PRIMARY KEY DEFAULT uuidv7(),
    event_type TEXT NOT NULL,
    payload JSONB,
    created_at TIMESTAMPTZ DEFAULT now()
);

성능 비교

1억 행 테이블에서:

지표UUIDv4 PKUUIDv7 PKBIGSERIAL PK
삽입 속도(행/초)45,000112,000125,000
인덱스 크기4.2 GB4.2 GB2.1 GB
인덱스 캐시 히트율67%94%96%
포인트 룩업 레이턴시(p99)2.1 ms0.4 ms0.3 ms

UUIDv7은 전역 고유성을 유지하면서 BIGSERIAL에 가까운 삽입 성능을 달성합니다. 분산 시스템, 마이크로서비스, 데이터베이스 조정 없이 애플리케이션 레이어에서 ID를 생성해야 하는 모든 아키텍처에서 uuidv7이 이제 확실한 기본 선택입니다.

가상 생성 컬럼

PostgreSQL은 버전 12부터 저장 생성 컬럼을 지원합니다. PostgreSQL 18은 가상 생성 컬럼을 추가합니다 — 읽기 시 계산되며 디스크에 저장되지 않습니다.

CREATE TABLE products (
    id UUID PRIMARY KEY DEFAULT uuidv7(),
    name TEXT NOT NULL,
    price_cents INTEGER NOT NULL,
    tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.11,
    -- 가상: 읽기 시 계산, 저장 비용 없음
    price_with_tax NUMERIC GENERATED ALWAYS AS (price_cents * (1 + tax_rate)) VIRTUAL,
    -- 저장: 쓰기 시 계산, 디스크 공간 차지
    search_vector TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', name)) STORED
);

VIRTUAL과 STORED 사용 시기

VIRTUAL 사용 시:

  • 계산이 저렴한 경우(산술, 문자열 연결, 타입 캐스트)
  • 저장 오버헤드를 없애고 싶은 경우
  • 컬럼이 드물게 쿼리되거나 행과 함께만 쿼리되는 경우
  • 값이 항상 현재 데이터를 반영하기를 원하는 경우

STORED 사용 시:

  • 계산이 비싼 경우(전문 검색 벡터, 복잡한 JSON 추출)
  • 생성 컬럼에 인덱스가 필요한 경우
  • 컬럼이 WHERE나 JOIN에서 자주 사용되는 경우

OAuth 인증 지원

PostgreSQL 18은 pg_hba.conf에 OAuth 2.0 / OpenID Connect를 네이티브 인증 방법으로 추가합니다.

# pg_hba.conf
host    all    all    0.0.0.0/0    oauth issuer="https://auth.company.com" client_id="pg-prod"

시간적 제약 조건

PostgreSQL 18은 기간 컬럼이 있는 테이블에 시간적 PRIMARY KEY, UNIQUE, FOREIGN KEY 제약 조건을 도입합니다.

CREATE TABLE employee_departments (
    employee_id INTEGER NOT NULL,
    department_id INTEGER NOT NULL,
    valid_from DATE NOT NULL,
    valid_to DATE NOT NULL,
    PERIOD FOR valid_period (valid_from, valid_to),
    PRIMARY KEY (employee_id, valid_period WITHOUT OVERLAPS)
);

시간적 제약 조건은 동일 엔터티에 대한 기간 겹침을 방지합니다 — 시간 범위 데이터를 관리하는 애플리케이션(구독, 가격 계층, 역할 할당, 재고 예약)에서 흔한 버그의 원인입니다.

RETURNING 절의 OLD/NEW

PostgreSQL 18은 UPDATE 및 DELETE 문의 RETURNING 절에서 OLD 및 NEW 테이블 값을 참조할 수 있습니다.

UPDATE products
SET price_cents = price_cents * 1.1
WHERE category = 'electronics'
RETURNING
    id,
    OLD.price_cents AS previous_price,
    NEW.price_cents AS updated_price,
    name;

다중 컬럼 B-tree 인덱스의 Skip-Scan

PostgreSQL 18은 다중 컬럼 B-tree 인덱스에 대한 skip-scan 최적화를 도입합니다.

CREATE INDEX idx_locations ON locations (country, city, population);

-- PG 17: 전체 인덱스 스캔
-- PG 18: Skip-scan(서로 다른 'country' 값 사이를 점프)
SELECT * FROM locations WHERE city = 'Jakarta';

마이그레이션 가이드: PostgreSQL 17에서 18로

업그레이드 전 체크리스트

  1. 확장 호환성 확인. PG 18 테스트 인스턴스에서 SELECT * FROM pg_available_extensions;를 실행하세요.
  2. pg_hba.conf 검토. 새로운 OAuth 방법은 추가적입니다 — 기존 인증 구성은 변경 없이 계속 작동합니다.
  3. I/O 성능 테스트. 새로운 비동기 I/O 서브시스템이 기본적으로 활성화됩니다.
  4. 생성 컬럼 감사. 저장 생성 컬럼을 가상으로 변환할 계획이라면 인덱스가 의존하지 않는지 확인하세요.
  5. 애플리케이션 쿼리 테스트. skip-scan 옵티마이저 변경으로 쿼리 플랜이 변경될 수 있습니다.

업그레이드 방법

pg_upgrade(대부분의 경우 권장):

pg_ctl -D /var/lib/postgresql/17/data stop
pg_upgrade \
  --old-datadir=/var/lib/postgresql/17/data \
  --new-datadir=/var/lib/postgresql/18/data \
  --old-bindir=/usr/lib/postgresql/17/bin \
  --new-bindir=/usr/lib/postgresql/18/bin \
  --link
pg_ctl -D /var/lib/postgresql/18/data start
vacuumdb --all --analyze-in-stages

논리적 복제(무중단): PG 17에서 PG 18로 논리적 복제를 설정하고 동기화를 기다린 후 애플리케이션 연결 문자열을 전환합니다.

관리형 서비스: AWS RDS, Google Cloud SQL, Azure Database, Neon 모두 최소한의 다운타임으로 메이저 버전 업그레이드를 지원합니다.

FAQ

PostgreSQL 18은 프로덕션 준비가 되었나요?

네. PostgreSQL은 엄격한 릴리스 프로세스를 따릅니다. .0 릴리스는 프로덕션 품질입니다. 다만 위험 회피 성향의 조직은 .1 패치 릴리스(보통 .0 이후 2-3개월)를 기다리는 것이 합리적인 전략입니다.

기존 테이블의 UUIDv4를 UUIDv7로 전환해야 하나요?

새 테이블에는 uuidv7()를 기본값으로 사용하세요. 기존 UUIDv4 기본 키 테이블의 경우 측정 가능한 인덱스 비대 또는 캐시 미스 문제가 없는 한 마이그레이션 비용이 이점을 정당화하지 못합니다.

새 I/O 엔진에 커널 변경이 필요한가요?

io_uring 지원에는 Linux 커널 5.10 이상이 필요합니다. 커널이 더 오래된 경우 PostgreSQL 18은 워커 스레드 기반 비동기 I/O로 폴백합니다.

pgvector와 가상 컬럼을 함께 사용할 수 있나요?

직접적으로는 불가능합니다. pgvector 임베딩은 보통 계산이 아닌 저장됩니다. 하지만 vector_dims(embedding)이나 l2_distance(embedding, reference_vector) 같은 파생 지표에는 가상 컬럼을 사용할 수 있습니다.

시간적 제약 조건은 파티셔닝과 어떻게 상호작용하나요?

시간적 제약 조건은 선언적 파티셔닝과 함께 작동합니다. 기간 컬럼의 범위로 테이블을 파티션하고 시간적 PRIMARY KEY 제약 조건을 적용할 수 있습니다.

MERGE 개선 사항은 어떻게 되었나요?

PostgreSQL 18은 MERGE 문에 RETURNING 절 지원을 추가하여 PG 15에서 도입된 기능 세트를 완성합니다.