핵심 요약
PostgreSQL 18은 2003년 7.4 이후 첫 wire protocol 변경(3.2)을 포함한 대규모 릴리스다. 비동기 I/O로 일부 워크로드 최대 3배, uuidv7·OAuth·temporal constraints 같은 실무에 직접 영향 주는 기능이 한 번에 들어왔다.
- 비동기 I/O 서브시스템 (sequential scan·bitmap heap scan·VACUUM)
- Skip scan — 멀티컬럼 B-tree 인덱스 활용 범위 확대
uuidv7()내장 함수 — 시간 정렬 UUID- Virtual generated columns (default)
- RETURNING에 OLD/NEW
- OAuth 인증
- 업그레이드 시 통계 보존
1. 비동기 I/O — 왜 3배 빠른가
이전 PG는 한 페이지를 읽고 처리한 후 다음 페이지를 요청하는 동기 I/O였다. 18은 이미 다음 페이지들을 prefetch하면서 처리한다.
-- 18에서 활성화 확인
SHOW io_method; -- worker | io_uring | sync
-- 워크로드별 권장:
-- io_uring (Linux 5.1+) — 가장 빠름
-- worker — 안정성 우선
-- sync — 호환 모드| 워크로드 | 17 vs 18 (worker) |
|---|---|
| full table scan | 1.8~2.5x |
| VACUUM | 2.0~3.0x |
| bitmap heap scan | 1.5~2.0x |
| OLTP 단건 조회 | 변화 없음 |
2. uuidv7() — 드디어 인덱스 친화적 UUID
uuidv4의 가장 큰 문제는 랜덤이라 B-tree 인덱스 페이지가 끊임없이 split되는 것. uuidv7은 timestamp prefix가 있어 정렬 가능.
-- 17까지: 외부 라이브러리 또는 수동 구현
CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- v4
...
);
-- 18: 네이티브
CREATE TABLE orders (
id uuid PRIMARY KEY DEFAULT uuidv7(), -- 시간 정렬
...
);실측 효과
- INSERT 쓰루풋 +30~50% (페이지 split 감소)
- 같은 시간대 row가 같은 페이지 → 캐시 히트율↑
id로 ORDER BY ASC하면 시간순으로 정렬됨
3. Skip Scan — 멀티컬럼 인덱스 활용 확대
이전엔 idx (a, b)에서 WHERE b = 1은 인덱스를 못 썼다. 18은 a의 distinct 값이 적으면 자동 skip scan.
CREATE INDEX ON orders (status, created_at);
-- 17: status 조건이 없으면 seq scan
-- 18: status가 ('pending','paid','shipped') 같이 카디널리티 낮으면
SELECT * FROM orders WHERE created_at >= '2026-04-01';
-- → skip scan 자동 사용4. Virtual Generated Columns (default)
-- 17: STORED만 가능 (디스크에 저장)
CREATE TABLE products (
price_cents int,
tax_cents int GENERATED ALWAYS AS (price_cents * 0.1) STORED
);
-- 18: VIRTUAL이 default (계산만)
CREATE TABLE products (
price_cents int,
tax_cents int GENERATED ALWAYS AS (price_cents * 0.1) -- VIRTUAL
);
-- 디스크 절약, INSERT/UPDATE 빠름
-- 단, 인덱스는 STORED만 가능5. RETURNING의 OLD/NEW
UPDATE/DELETE 시 변경 전후 값을 한 번에 받기. 트리거나 별도 SELECT 없이 audit log 가능.
UPDATE accounts
SET balance = balance - 100
WHERE id = 42
RETURNING old.balance AS before, new.balance AS after;
-- 결과: before=500, after=400
-- MERGE에서도
MERGE INTO inventory ...
RETURNING merge_action(), old.qty, new.qty;6. OAuth 인증
-- pg_hba.conf
host all all 0.0.0.0/0 oauth issuer="https://accounts.google.com" scope="openid email" map=oauth_users
-- pg_ident.conf
oauth_users /^(.+)@example\.com$ \1각 클라우드 IdP를 직접 통합. 비밀번호 회전·관리에서 해방.
7. Temporal Constraints
예약 시스템에서 같은 자원이 시간 겹치게 들어가는 걸 DB 레벨에서 차단:
CREATE TABLE bookings (
id serial PRIMARY KEY,
room_id int,
during tsrange,
EXCLUDE USING gist (room_id WITH =, during WITH &&) -- 17까지 가능
);
-- 18: PRIMARY KEY/UNIQUE/FOREIGN KEY에도 temporal
CREATE TABLE rates (
product_id int,
during tsrange,
rate numeric,
PRIMARY KEY (product_id, during WITHOUT OVERLAPS)
);8. 업그레이드 시 통계 보존
17 이전엔 pg_upgrade 후 ANALYZE 안 하면 쿼리 플래너가 엉망이라 한동안 성능 저하. 18은 통계 함께 마이그레이션.
pg_upgrade --copy-statistics ...
# 업그레이드 직후 정상 성능9. 마이그레이션 체크리스트
- Extension 호환성 확인 (PostGIS·pgvector 등 18 대응판 확인)
- uuidv4 → uuidv7 마이그레이션 검토 (새 테이블만, 기존은 그대로)
- STORED generated column → VIRTUAL 후보 발굴
io_method설정 (Linux는 io_uring)- OAuth 도입 검토 (특히 multi-tenant SaaS)
10. 함정·주의점
- 비동기 I/O는 IOPS 압박 큰 환경에선 더 빨라지지만, 디스크 대역폭 한계 환경에선 별 차이 없을 수 있다
- Virtual generated column에 인덱스 못 만든다 — 인덱스 필요하면 STORED
- uuidv7도 보안 식별자로 쓸 땐 주의 — timestamp가 노출됨. URL 식별자엔 ULID/snowflake 권장
- protocol 3.2는 옵션. 3.0 클라이언트도 계속 동작
자주 묻는 질문
RDS·Aurora는 언제?
AWS는 통상 6~12개월 후. 18은 2025년 9월 GA → 2026년 상반기 RDS, 후반기 Aurora 예상.
uuidv7 vs ULID vs snowflake?
uuidv7은 표준 + DB 네이티브로 가장 깔끔. ULID는 외부 라이브러리 필요하지만 base32라 짧음. snowflake는 워커 ID 관리 필요.
17에서 18로 다운타임?
pg_upgrade는 수 분~십수 분. logical replication 사용하면 거의 0. 통계 보존 덕에 업그레이드 후 워밍업 불필요.

댓글 0