출시 일정과 이 글의 기준
PostgreSQL 19는 2026년 9월 24일 베타 4가 나왔고, 공식 공지에 따르면 다음 단계는 10월 초 릴리스 후보(RC)이며 테스트 결과에 따라 정식 버전(GA)도 10월에 나올 수 있습니다. 베타 단계라 세부 동작은 아직 바뀔 수 있습니다. 이 글은 베타 4 시점의 공식 릴리스 노트를 기준으로 합니다.
먼저 알아 둘 점이 있습니다. 베타 4에서 다음 기능들은 19에서 빠졌습니다. 안정성과 예측 가능한 일정을 위해 이후 메이저 버전으로 미룬 것입니다.
- SQL/PGQ(속성 그래프 쿼리)
- 데이터 체크섬의 온라인 켜기·끄기
FOR PORTION OF를 쓰는 시간 범위 UPDATE/DELETEALTER TABLE ... MERGE PARTITIONS/SPLIT PARTITIONS
개발 중에 소개된 해설 글 중에는 이 기능들이나 GROUP BY ALL처럼 베타 4 릴리스 노트에 없는 기능을 19의 기능으로 다룬 것이 있습니다. 업그레이드 계획은 릴리스 노트로 확인하세요.
1. REPACK: VACUUM FULL과 CLUSTER를 합치고, 잠금 없이 실행
테이블이 업데이트·삭제로 부풀면 공간을 되찾기 위해 VACUUM FULL이나 CLUSTER를 썼지만, 둘 다 실행 내내 테이블을 잠가 서비스 중에는 쓰기 어려웠습니다. 19에는 두 명령을 대신하는 REPACK이 생겼고, CONCURRENTLY 옵션을 주면 읽기·쓰기를 막지 않고 테이블을 다시 씁니다.
-- 공간 회수 (실행 중에도 읽기·쓰기 가능)
REPACK (CONCURRENTLY, VERBOSE) orders;
-- 인덱스 순서대로 물리 정렬(클러스터링)까지
REPACK (CONCURRENTLY) orders USING INDEX orders_ordered_at_idx;
동작 방식은 이렇습니다. 새 테이블을 만드는 동안 들어온 변경을 논리 디코딩으로 모아 반영하고, 가장 강한 잠금(ACCESS EXCLUSIVE)은 파일을 바꿔 끼우는 짧은 순간에만 잡습니다.
설계 관점에서 중요한 것은 CONCURRENTLY를 쓸 수 없는 조건입니다.
- 기본 키도, 인덱스 기반 복제 식별자(replica identity)도 없는 테이블
- 파티션된 테이블,
UNLOGGED테이블 - 트랜잭션 블록 안에서 실행할 때
max_repack_replication_slots가 복제 슬롯을 더 허용하지 않을 때
즉 모든 테이블에 기본 키를 두는 것이 이제 운영 도구를 쓸 수 있느냐의 문제가 됩니다. 또한 공식 문서는 CONCURRENTLY가 MVCC 안전하지 않다고 경고하므로, 실행 중 오래 걸린 트랜잭션이 보는 데이터에 대한 주의 사항(문서의 13.6절)을 읽고 쓰세요.
2. CHECK 제약도 ENFORCED / NOT ENFORCED 전환
PostgreSQL 18부터 CHECK와 외래 키 제약을 NOT ENFORCED로 선언할 수 있게 되었습니다. 19에서는 ALTER CONSTRAINT로 CHECK 제약의 적용 여부를 나중에 바꿀 수 있습니다(18까지는 외래 키만 가능). ENFORCED로 바꾸면 기존 행이 규칙을 지키는지 확인한 뒤 새 행에도 적용합니다.
-- 기존 데이터에 위반이 있을 수 있는 규칙을 일단 선언만
ALTER TABLE orders
ADD CONSTRAINT orders_amount_positive CHECK (amount_krw > 0) NOT ENFORCED;
-- 위반 데이터를 정리한 뒤 적용
ALTER TABLE orders ALTER CONSTRAINT orders_amount_positive ENFORCED;
규칙을 스키마에 먼저 문서화해 두고, 데이터 정리가 끝난 뒤 실제로 강제하는 단계적 마이그레이션에 쓸 수 있습니다.
3. 외래 키 확인이 빨라진다
릴리스 노트 개요는 성능 개선 항목으로 더 빠른 외래 키 확인을 들고 있습니다. 외래 키가 많은 정규화된 스키마에서 쓰기 비용이 줄어드는 방향의 변화입니다(개선 폭은 작업 부하마다 다르므로 직접 측정해 보세요). 이 밖에 CREATE SCHEMA 안에서 아직 만들지 않은 테이블을 외래 키로 앞서 참조할 수 있게 되었습니다.
4. 업그레이드 전에 확인할 동작 변화
호환성 항목 중 애플리케이션에 영향을 줄 수 있는 것들입니다.
standard_conforming_strings가 항상 켜짐: 일반 문자열 리터럴 안의 백슬래시를 이스케이프로 해석하던 동작에 기대는 코드가 있다면 확인이 필요합니다- 결과 행이 없는
json_array()호출이 NULL 대신 빈 JSON 배열을 반환:IS NULL로 "없음"을 판별하던 쿼리는 결과가 바뀝니다 max_locks_per_transaction기본값 64 → 128- 데이터베이스·역할·테이블스페이스 이름에 줄바꿈(CR·LF) 금지
inet·cidr의 기본 인덱스 opclass가 btree_gist에서 GiST로 변경: btree_gist 확장으로 이 타입에 인덱스를 만들어 왔다면 확인하세요
그 밖에 bytea와 uuid 사이 캐스트가 허용되어, 바이너리로 받은 UUID를 바로 변환할 수 있습니다.
5. 복제와 읽기 일관성
시퀀스 값 논리 복제. 논리 복제가 이제 시퀀스 값도 복제합니다. 논리 복제로 메이저 버전 업그레이드를 할 때 전환 직전 시퀀스 값을 수동으로 맞추던 작업이 줄어듭니다. 또한 wal_level이 replica이면 서버 재시작 없이 논리 복제를 켤 수 있습니다.
WAIT FOR LSN으로 "내가 쓴 것을 읽기". 쓰기는 기본 서버로, 읽기는 복제본으로 보내는 구조에서는 방금 쓴 데이터가 복제본에 아직 없을 수 있습니다. 새 WAIT 명령으로 복제본에서 특정 WAL 위치까지 반영될 때까지 기다릴 수 있습니다.
-- 기본 서버: 쓰기 직후 현재 WAL 위치 확인
SELECT pg_current_wal_lsn(); -- 예: 0/306EE20
-- 복제본: 그 위치까지 반영된 뒤 읽기 (최대 1초 대기)
WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '1s');
SELECT * FROM orders WHERE id = 1001;
NO_THROW 옵션을 주면 시간 초과 시 오류 대신 상태(success, timeout 등)를 돌려받습니다.
6. 그 밖의 변화
- autovacuum이 병렬 작업자로 인덱스를 정리하고, 가장 필요한 테이블부터 처리하는 점수 체계를 도입
- jsonpath 문자열 메서드 추가:
lower(),upper(),initcap(),replace(),split_part(),btrim()·ltrim()·rtrim() - 플래너 결정을 고정·제어하는
pg_plan_advice확장과, 쿼리별로 자동 적용하는pg_stash_advice
Erdmates에서 미리 점검하기
19로 올라가기 전에 스키마를 ERD로 가져와 보면, 기본 키 표시가 없는 테이블(REPACK CONCURRENTLY를 쓸 수 없는 테이블)과 외래 키 관계를 한눈에 확인할 수 있습니다. Erdmates는 PostgreSQL의 CREATE TABLE·COMMENT ON 문을 가져와 ERD로 그려 줍니다.
마무리
PostgreSQL 19에서 스키마 설계자가 가장 먼저 챙길 것은 세 가지입니다. 모든 테이블에 기본 키를 두어 REPACK CONCURRENTLY를 쓸 수 있게 하고, 단계적 마이그레이션에 CHECK 제약의 NOT ENFORCED → ENFORCED 전환을 활용하고, standard_conforming_strings·json_array() 같은 동작 변화를 업그레이드 전에 테스트하는 것입니다. 정식 출시 전까지는 기능 목록이 바뀔 수 있으니 최종 릴리스 노트를 다시 확인하세요.