한 줄 요약

Christophe Pettus가 The Build에서 정리한 PostgreSQL 19 Beta Big 4는 모두 “피처 노트가 아닌, 운영 시나리오를 바꾸는 변경“이라는 공통점이 있다. 신규 syntax 한 줄로 끝나지 않고, DBA의 메모리 계산식·장애 시나리오·튜닝 기본값을 다시 그리게 만든다.

업데이트 (2026-06-04): PostgreSQL 19 Beta 1이 공식 릴리스됐다. 이 글이 예고한 Big 4는 모두 Beta 1에 그대로 담겼다 — 64bit MultiXact members(Make multixid members 64-bit), 병렬 autovacuum worker(autovacuum_max_parallel_workers), UPDATE/DELETE FOR PORTION OF, JIT 기본 비활성화. 정식 출시는 9월~10월 예정이며, 그사이 release candidate가 한 차례 이상 나온다.

PostgreSQL 19는 9월 정식 출시 예정이다. 이번 글은 Pettus의 Big 4 큐레이션을 출발점으로 삼아, 거기에 DBA 점검 항목 · 메모리 계산식 · SQL 예시 · 업그레이드 체크리스트를 보탠 글이다. 네 가지 선정은 Pettus의 시각이고, 운영 점검·메커니즘 해설·업그레이드 절차는 기존 PG19 시리즈 5편이 다루지 않은 부분을 채우는 보완·확장이다.

왜 이 Big 4인가

PostgreSQL 19의 변경 목록은 CommitFest 5회분의 수백 건이다. 그중 DBA가 실제로 체감하는 변화는 보통 둘 중 하나다.

  • 장애 회피 — 지금까지 운영 매뉴얼에 “이거 터지면 끝"으로 적혀 있던 시나리오가 사라진다.
  • 튜닝 기본값 변경 — 업그레이드 직후 워크로드 성격에 따라 plan 시간 · 메모리 · 비상 상황이 달라진다.

Pettus가 꼽은 Big 4는 정확히 그 두 축에 들어간다. 새 syntax · 새 알고리즘이 아니라, 기존 운영 매뉴얼을 다시 쓰게 만드는 변경들이다.

변경영향
64bit MultiXact members장애 회피“긴급 vacuum"이라는 운영 시나리오가 사라진다
병렬 autovacuum 인덱스 worker튜닝 기본값 변경최악 메모리 사용량 식이 달라진다
UPDATE/DELETE FOR PORTION OF신규 운영 영역row trigger · cascading FK 동작에 새 사각지대
jit = off (기본)튜닝 기본값 변경OLAP는 명시 활성화 안 하면 회귀

1. 64bit MultiXact members — “긴급 vacuum"이라는 운영 시나리오의 끝

무엇이 바뀌었나

MultiXact 멤버 카운터가 32bit에서 64bit로 확장됐다. 4 billion(약 40억) 멤버 공간 고갈 시나리오가 사실상 사라진다.

DBA가 왜 체감하나

PostgreSQL에는 DBA 사이에서 “이게 터지면 끝” 으로 통하는 시나리오가 몇 개 있다. MultiXact wraparound는 거기 한참 머물러 있었다.

SELECT ... FOR SHARE, foreign-key check 같은 공유 row lock이 같은 행에 여러 트랜잭션에서 동시에 걸리면, PostgreSQL은 그 잠금 정보를 묶어서 MultiXact라는 별도 구조에 기록한다. 각 MultiXact는 멤버 슬롯을 소비한다. 그 멤버 카운터의 자료형이 PostgreSQL 19에서 처음으로 확장됐다.

구분타입최대값
기존 (PG 18 이하)uint32 (32비트)약 42억 9천만
변경 (PG 19~)uint64 (64비트)약 1844경

42억은 운영 부하가 큰 워크로드(공유 lock + FK check 누적)에서 실제로 도달할 수 있는 수치다. 1844경은 사실상 도달이 불가능한 영역으로 넘어간다.

주의 — XID · mxid wraparound는 그대로다

DBA가 흔히 “wraparound"라고 묶어 부르는 경우는 사실 셋이다. 이번 변경은 그중 하나에 한정된다.

wraparound 종류PG 19에서 변경운영 영향
MultiXact members 카운터uint32uint64사실상 해결
MultiXact ID (mxid)변경 없음 (32bit)autovacuum 의존, anti-wraparound vacuum 그대로
Transaction ID (xid)변경 없음 (32bit)autovacuum의 freeze 책임 그대로

PostgreSQL의 오래된 숙제 — XID wraparound · mxid wraparound — 는 PostgreSQL 19에서도 살아 있다. autovacuum이 주기적으로 freeze를 수행해야 하는 이유가 정확히 이것이다. 이번 글이 다루는 “해결된 wraparound"는 MultiXact members 카운터 하나뿐임을 분명히 해 둔다.

64bit XID 확장은 2018년부터 PostgreSQL hackers 메일링 리스트에서 논의돼 왔다. Postgres Pro가 PostgreSQL 15 시기에 실험적 패치 시리즈로 동작 확인까지 갔지만, tuple 헤더 크기 증가 · 모든 index format 재설계 · WAL format 변경 같은 광범위한 ripple effect 때문에 main 브랜치 commit으로 이어지지 않았다. PostgreSQL 19에서도 같은 상태다. PostgreSQL의 가장 오래된 숙제 중 하나가 한 라운드 더 미뤄졌고, 그 사이 DBA는 autovacuum freeze 튜닝과 pg_visibility · pg_class.relfrozenxid 모니터링을 손에서 놓을 수 없다.

운영 영향은 이렇게 정리된다.

항목PG 18까지PG 19 Beta
MultiXact members 고갈 시새 트랜잭션 거부 + offline emergency VACUUM사실상 도달 불가
XID · mxid wraparoundautovacuum freeze로 방어그대로 — 모니터링 유지
운영 매뉴얼 항목MultiXact members 항목은 “이거 터지면 끝” 목록의 한 줄members 항목만 삭제 후보. XID · mxid는 유지

42억 한계에 도달하면 DBA가 마주하는 그림은 단순했다. 새 트랜잭션이 거부되고, 복구 경로는 offline emergency VACUUM 하나. Pettus의 한 줄이 이를 짚는다.

“When exhausted, the system would refuse new transactions, and the only recovery path was an emergency VACUUM.”

이론적으로 wraparound 수학은 2^64에서도 여전히 존재한다. 다만 같은 워크로드를 우주의 나이만큼 굴려도 도달이 어렵다. 32bit 공간 도달 사례는 운영 사고 보고서들에 종종 등장했지만, 64bit는 실무 카탈로그에서 제외할 수 있다.

DBA가 할 일

  • 운영 매뉴얼·런북에서 “MultiXact wraparound” 비상 절차 항목을 “PG 19부터는 도달 불가” 로 갱신
  • 단, PostgreSQL 18 이하 운영 중인 클러스터는 그대로 이 시나리오가 살아 있다 — 모니터링 대시보드 유지

Beta 1에는 이 부분을 보강하는 변경도 함께 들어왔다. multixact 활동을 보여 주는 pg_get_multixact_stats() 함수가 추가됐고, xid · multixact wraparound 경고 임계값이 기존 4천만에서 1억 건 남았을 때로 올라갔다(클라이언트와 서버 로그 양쪽에 경고). 여전히 살아 있는 xid · mxid wraparound를 그만큼 더 일찍 알아챌 수 있다.

2. 병렬 autovacuum 인덱스 worker — 메모리 계산식의 변경

무엇이 바뀌었나

새 GUC autovacuum_max_parallel_workers가 추가된다. autovacuum이 단일 테이블의 인덱스 정리를 여러 worker로 병렬 처리한다. PostgreSQL 17에서 도입된 manual VACUUM의 병렬 인덱스 정리가 autovacuum 경로까지 확장됐다.

DBA가 왜 체감하나

인덱스가 많이 붙은 wide table을 운영해 본 사람은 안다 — autovacuum이 돌면 heap 정리는 빠른데 인덱스 정리 단계에서 한참 멈춰 있다. 단일 worker가 인덱스 N개를 순차로 도는 구조였기 때문이다.

PostgreSQL 19에서는 같은 단계에 worker가 동시에 들어간다. wide-and-many-indexes 테이블의 autovacuum 시간이 줄어들고, vacuum 누적 부담이 풀린다.

-- 새 GUC (값은 예시)
ALTER SYSTEM SET autovacuum_max_parallel_workers = 4;
SELECT pg_reload_conf();

주의 — 최악 메모리 식이 달라진다

같은 변경이 운영 부담을 새로 만드는 지점도 있다. 각 병렬 worker는 자기 몫의 maintenance_work_mem을 따로 잡는다.

항목PG 18까지PG 19 Beta
autovacuum 최악 메모리autovacuum_max_workers × maintenance_work_memautovacuum_max_workers × autovacuum_max_parallel_workers × maintenance_work_mem

autovacuum_max_workers=3, autovacuum_max_parallel_workers=4, maintenance_work_mem=1GB인 환경에서 최악 메모리 사용량은 3 × 4 × 1GB = 12GB가 된다. PostgreSQL 18까지의 3GB와 4배 차이다.

DBA는 PostgreSQL 19 업그레이드 전에 세 가지를 확인한다.

  1. maintenance_work_mem 현재 값
  2. autovacuum_max_workers 현재 값
  3. 위 두 곱에 새 GUC 곱을 더한 값이 시스템 메모리에 맞는가

maintenance_work_mem을 1GB 이상으로 키워 놓은 운영 환경(분석 워크로드 · 큰 인덱스 재정렬 빈도가 잦은 곳)은 특히 다시 계산해야 한다.

“명백한 이득"이 보이는 경우는 한정적 — serial 인덱스 정리가 vacuum의 병목이던 wide table에서 가장 큰 폭으로 줄어든다.

DBA가 할 일

  • 업그레이드 전 메모리 식 재계산. RAM이 빠듯하면 autovacuum_max_parallel_workers를 기본보다 낮춰 시작
  • wide table · 인덱스 다수 테이블에서 autovacuum 지속 시간 모니터링 (pg_stat_progress_vacuum)
  • 한꺼번에 너무 많은 worker가 깨어나 I/O 포화되지 않도록 autovacuum_vacuum_cost_limit 검토

3. UPDATE ... FOR PORTION OF — SQL:2011 시간 범위가 PostgreSQL로

무엇이 바뀌었나

시간 범위(period) 컬럼을 가진 테이블에서 부분 범위만 UPDATE 또는 DELETE할 수 있게 됐다. SQL:2011 표준 syntax다.

-- 가격 이력 테이블 (period 컬럼 사용 가정)
CREATE TABLE price_history (
    product_id int,
    price numeric,
    valid_period daterange,
    PERIOD FOR valid_period (valid_from, valid_to)
);

-- 2026-06-01 ~ 2026-06-15 구간만 가격 변경
UPDATE price_history
FOR PORTION OF valid_period
FROM DATE '2026-06-01' TO DATE '2026-06-15'
SET price = price * 0.9
WHERE product_id = 42;

이전에는 DBA가 직접 row를 자르고 새 row를 끼워 넣는 SQL을 손으로 썼다. PostgreSQL 19부터는 한 문장으로 끝난다. 건드리지 않은 범위의 row는 자동으로 보존된다.

DBA가 왜 체감하나

가격 이력, 직원 직책 이력, 권한 이력, 보험료 이력 — 시간 범위 컬럼을 두는 모델은 흔하다. 매번 손으로 자르는 SQL을 짜는 대신 syntax 한 줄로 정리되는 경우가 늘어난다.

다만 — 새 운영 사각지대도 함께 생긴다.

주의 — row trigger · cascading FK

Pettus가 직접 짚는다.

“a FOR PORTION OF update can fire row triggers on rows that did not exist when the statement started.”

(FOR PORTION OF UPDATE는 문이 시작될 때 존재하지 않던 row에 대해서도 row trigger를 발화시킬 수 있다.)

내부적으로 FOR PORTION OF는 원본 row를 잘라 새 row 두 개(또는 세 개)로 분리하고, 그중 하나에 UPDATE를 적용한다. 그 결과 — DELETE 트리거가 원본 row에, INSERT 트리거가 새 보존 row 둘에, UPDATE 트리거가 갱신된 부분 row에 각각 발화한다.

cascading foreign-key 동작은 한 단계 더 복잡해진다. parent 행의 시간 범위가 잘리면 child의 FK 동작이 어떻게 따라가는지 production에서 테스트하지 않고 올리면 사고 가능성이 크다. parent의 부분 UPDATE가 child를 DELETE해야 하는가, CASCADE해야 하는가, RESTRICT해야 하는가 — 운영 정책 결정이 SQL 한 줄 안에 묻혀 들어간다.

DBA가 할 일

  • FOR PORTION OF를 production에 올리기 전에 row trigger · FK · audit trigger 동작을 staging에서 전부 확인
  • 트리거가 row 단위로 카운트를 세는 코드(예: audit row 개수 검증)는 PostgreSQL 19에서 결과가 다를 수 있다 — 재검토

4. jit = off 기본값 — PostgreSQL 12 이후 처음으로 다시 꺼진다

무엇이 바뀌었나

Just-in-time compilation 기본값이 off로 바뀐다. PostgreSQL 12에서 jit = on이 기본값으로 들어온 이후 처음 있는 방향 전환이다.

DBA가 왜 체감하나

이 변경은 워크로드 성격에 따라 정반대 결과를 만든다.

워크로드PG 18까지PG 19 Beta
OLTP (짧은 쿼리 다수)JIT plan 오버헤드가 매 쿼리마다 누적plan 시간 단축, 응답성 개선
OLAP (긴 분석 쿼리)JIT 컴파일 후 실행 가속명시적으로 jit = on 안 켜면 회귀

Pettus는 OLAP 회귀의 크기를 한 줄로 묘사한다.

“a six-minute report now takes nineteen.”

(6분짜리 리포트가 19분 걸리게 된다.)

3배 회귀 사례를 예고하는 대목이다. OLAP 클러스터를 PostgreSQL 19로 올리면서 jit = on을 명시 설정하지 않으면, 출시 직후 가장 먼저 받는 분기 보고서가 평소보다 한참 늦게 도착한다.

DBA가 할 일

  • OLTP 클러스터: 그대로 두면 자연스럽게 plan 효율 개선. 별도 설정 불필요.
  • OLAP · 분석 클러스터: 업그레이드 직후 ALTER SYSTEM SET jit = on; 실행. 또는 분석 사용자의 ALTER ROLE ... SET jit = on;으로 user-level 지정.
  • 혼합 워크로드: pgBouncer 등의 connection pool에서 분석 트래픽 분리한 뒤 그쪽만 jit = on 적용

왜 방향이 바뀌었는가

PostgreSQL 12에서 JIT를 기본 활성화한 동기는 “분석 쿼리에서 측정 가능한 가속이 있다"였다. 하지만 운영 통계가 쌓이면서 — OLTP가 절대 다수인 PostgreSQL 운영 현실에서 plan 시간 오버헤드 손실이 컴파일 가속 이득을 자주 초과한다는 점이 드러났다. 6년 만에 측정 기반으로 기본값이 복귀하는 셈이다.

PostgreSQL 19 업그레이드 체크리스트 (이번 Big 4)

업그레이드 전 다음을 점검한다.

점검 항목명령 / 확인
maintenance_work_mem 현재 값SHOW maintenance_work_mem;
autovacuum_max_workers 현재 값SHOW autovacuum_max_workers;
최악 메모리 사용량 재계산autovacuum_max_workers × autovacuum_max_parallel_workers × maintenance_work_mem
OLAP/혼합 워크로드 식별분석 ETL · BI 도구 사용 여부
시간 범위 컬럼 사용 테이블 식별\d+ 로 PERIOD 컬럼 점검
MultiXact 모니터링 운영 매뉴얼“PG 19부터 도달 불가” 주석

업그레이드 직후에는 다음을 즉시 확인.

-- 1. autovacuum 동작 확인
SELECT * FROM pg_stat_progress_vacuum;

-- 2. JIT 설정 확인 (OLAP면 명시 on)
SHOW jit;

-- 3. 새 GUC 확인
SHOW autovacuum_max_parallel_workers;

정리

변경DBA가 얻는 것DBA가 새로 봐야 하는 것
64bit MultiXact members최악 wraparound 시나리오 제거PG 18 이하 클러스터 모니터링은 유지
병렬 autovacuum 인덱스 workerwide table autovacuum 시간 단축최악 메모리 사용량 식이 달라진다
UPDATE/DELETE FOR PORTION OF시간 범위 SQL 한 줄로 정리row trigger · cascading FK 동작 재검증
jit = off (기본)OLTP plan 시간 단축OLAP는 명시 활성화 안 하면 회귀

PostgreSQL 19는 화려한 신기능이 아니라 운영 매뉴얼을 다시 쓰게 만드는 릴리스다. DBA 입장에서 이번 Big 4는 9월 정식 출시 전에 staging에서 한 번 돌려 보고, 메모리 식·튜닝 기본값·운영 매뉴얼 세 가지를 같이 갱신해야 한다.

기존 PostgreSQL 19 새 기능 총정리PostgreSQL 19 시리즈 4편“어떤 기능이 들어오는가” 시각이었고, 이번 글은 “어떤 운영이 달라지는가” 시각이다. 두 글을 같이 두면 PostgreSQL 19를 올리기 전 점검할 항목이 한곳에 모인다.

참고