한 줄 요약
Christophe Pettus가 The Build에서 정리한 PostgreSQL 19 Beta Big 4는 모두 “피처 노트가 아닌, 운영 시나리오를 바꾸는 변경“이라는 공통점이 있다. 신규 syntax 한 줄로 끝나지 않고, 운영자의 메모리 계산식·장애 시나리오·튜닝 기본값을 다시 그리게 만든다.
PostgreSQL 19는 9월 정식 출시 예정이다. 이번 글은 Pettus의 Big 4 큐레이션을 출발점으로 삼아, 거기에 운영자 점검 항목 · 메모리 계산식 · SQL 예시 · 업그레이드 체크리스트를 보탠 글이다. 네 가지 선정은 Pettus의 시각이고, 운영 점검·메커니즘 해설·업그레이드 절차는 기존 PG19 시리즈 5편이 다루지 않은 자리를 채우는 보완·확장이다.
왜 이 Big 4인가
PostgreSQL 19의 변경 목록은 CommitFest 5회분의 수백 건이다. 그중 운영자가 실제로 체감하는 자리는 보통 둘 중 하나다.
- 장애 회피 — 지금까지 운영 매뉴얼에 “이거 터지면 끝"으로 적혀 있던 시나리오가 사라진다.
- 튜닝 기본값 변경 — 업그레이드 직후 워크로드 성격에 따라 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억) 멤버 공간 고갈 시나리오가 사실상 사라진다.
왜 운영자가 느끼나
PostgreSQL에는 운영자 사이에서 “이게 터지면 끝” 으로 통하는 시나리오가 몇 개 있다. MultiXact wraparound는 그 자리에 한참 머물러 있었다.
SELECT ... FOR SHARE, foreign-key check 같은 공유 row lock이 같은 행에 여러 트랜잭션에서 동시에 걸리면, PostgreSQL은 그 잠금 정보를 묶어서 MultiXact라는 별도 구조에 기록한다. 각 MultiXact는 멤버 슬롯을 소비한다. 그 멤버 카운터의 자료형이 PG 19에서 처음으로 확장됐다.
| 구분 | 타입 | 최대값 |
|---|---|---|
| 기존 (PG 18 이하) | uint32 (32비트) | 약 42억 9천만 |
| 변경 (PG 19~) | uint64 (64비트) | 약 1844경 |
42억은 운영 부하가 큰 워크로드(공유 lock + FK check 누적)에서 실제로 도달 가능한 수치다. 1844경은 — 단위 감각상 도달이 불가능한 영역으로 옮겨간다.
주의 — XID · mxid wraparound는 그대로다
운영자가 흔히 “wraparound"라고 묶어 부르는 자리는 사실 셋이다. 이번 변경은 그중 한 자리에 한정된다.
| wraparound 종류 | PG 19에서 변경 | 운영 영향 |
|---|---|---|
| MultiXact members 카운터 | uint32 → uint64 | 사실상 해결 |
| MultiXact ID (mxid) | 변경 없음 (32bit) | autovacuum 의존, anti-wraparound vacuum 그대로 |
| Transaction ID (xid) | 변경 없음 (32bit) | autovacuum의 freeze 책임 그대로 |
PostgreSQL의 오래된 숙제 — XID wraparound · mxid wraparound — 는 PG 19에서도 살아 있다. autovacuum이 주기적으로 freeze를 수행해야 하는 이유가 정확히 이것이다. 이번 글이 다루는 “해결된 wraparound"는 MultiXact members 카운터 하나뿐임을 분명히 해 둔다.
64bit XID 확장은 2018년부터 PostgreSQL hackers 메일링 리스트에서 논의돼 왔다. Postgres Pro가 PG 15 시기에 실험적 패치 시리즈로 동작 확인까지 갔지만, tuple 헤더 크기 증가 · 모든 index format 재설계 · WAL format 변경 같은 광범위한 ripple effect 때문에 main 브랜치 commit으로 이어지지 않았다. PG 19에서도 같은 상태다. PostgreSQL의 가장 오래된 숙제 중 하나가 한 라운드 더 미뤄진 자리고, 그 사이 운영자는 autovacuum freeze 튜닝과 pg_visibility · pg_class.relfrozenxid 모니터링을 손에서 놓을 수 없다.
운영 영향은 이렇게 정리된다.
| 항목 | PG 18까지 | PG 19 Beta |
|---|---|---|
| MultiXact members 고갈 시 | 새 트랜잭션 거부 + offline emergency VACUUM | 사실상 도달 불가 |
| XID · mxid wraparound | autovacuum freeze로 방어 | 그대로 — 모니터링 유지 |
| 운영 매뉴얼 자리 | MultiXact members 항목은 “이거 터지면 끝” 목록의 한 자리 | members 항목만 삭제 후보. XID · mxid는 유지 |
42억 한계에 도달하면 운영자가 마주하는 그림은 단순했다. 새 트랜잭션이 거부되고, 복구 경로는 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는 실무 카탈로그에서 제외할 수 있는 자리다.
운영자가 할 일
- 운영 매뉴얼·런북에서 “MultiXact wraparound” 비상 절차 항목을 “PG 19부터는 도달 불가” 로 갱신
- 단, PG 18 이하 운영 중인 클러스터는 그대로 이 시나리오가 살아 있다 — 모니터링 대시보드 유지
2. 병렬 autovacuum 인덱스 worker — 메모리 계산식의 변경
무엇이 바뀌었나
새 GUC autovacuum_max_parallel_workers가 추가된다. autovacuum이 단일 테이블의 인덱스 정리를 여러 worker로 병렬 처리한다. PG 17에서 도입된 manual VACUUM의 병렬 인덱스 정리가 autovacuum 경로까지 확장됐다.
왜 운영자가 느끼나
인덱스가 많이 붙은 wide table을 운영해 본 사람은 안다 — autovacuum이 돌면 heap 정리는 빠른데 인덱스 정리 단계에서 한참 멈춰 있다. 단일 worker가 인덱스 N개를 순차로 도는 구조였기 때문이다.
PG 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_mem | autovacuum_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가 된다. PG 18까지의 3GB와 4배 차이다.
운영자는 PG 19 업그레이드 전에 세 가지를 확인한다.
maintenance_work_mem현재 값autovacuum_max_workers현재 값- 위 두 곱에 새 GUC 곱을 더한 값이 시스템 메모리에 맞는가
maintenance_work_mem을 1GB 이상으로 키워 놓은 운영 환경(분석 워크로드 · 큰 인덱스 재정렬 빈도가 잦은 곳)은 특히 다시 계산해야 한다.
“명백한 이득"이 보이는 자리는 한정적 — serial 인덱스 정리가 vacuum의 병목이던 wide table에서 가장 큰 폭으로 줄어든다.
운영자가 할 일
- 업그레이드 전 메모리 식 재계산. 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;
이전에는 운영자가 직접 row를 자르고 새 row를 끼워 넣는 SQL을 손으로 썼다. PG 19부터는 한 문장으로 끝난다. 건드리지 않은 범위의 row는 자동으로 보존된다.
왜 운영자가 느끼나
가격 이력, 직원 직책 이력, 권한 이력, 보험료 이력 — 시간 범위 컬럼을 두는 모델은 흔하다. 매번 손으로 자르는 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 한 줄 안에 묻혀 들어간다.
운영자가 할 일
FOR PORTION OF를 production에 올리기 전에 row trigger · FK · audit trigger 동작을 staging에서 전부 확인- 트리거가 row 단위로 카운트를 세는 코드(예: audit row 개수 검증)는 PG 19에서 결과가 다를 수 있다 — 재검토
4. jit = off 기본값 — PG 12부터 처음으로 다시 꺼지는 자리
무엇이 바뀌었나
Just-in-time compilation 기본값이 off로 바뀐다. PG 12에서 jit = on이 기본값으로 들어온 이후 처음 있는 방향 전환이다.
왜 운영자가 느끼나
이 변경은 워크로드 성격에 따라 정반대 결과를 만든다.
| 워크로드 | 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 클러스터를 PG 19로 올리면서 jit = on을 명시 설정하지 않으면, 출시 직후 가장 먼저 받는 분기 보고서가 평소보다 한참 늦게 도착한다.
운영자가 할 일
- OLTP 클러스터: 그대로 두면 자연스럽게 plan 효율 개선. 별도 설정 불필요.
- OLAP · 분석 클러스터: 업그레이드 직후
ALTER SYSTEM SET jit = on;실행. 또는 분석 사용자의ALTER ROLE ... SET jit = on;으로 user-level 지정. - 혼합 워크로드: pgBouncer 등의 connection pool에서 분석 트래픽 분리한 뒤 그쪽만
jit = on적용
왜 방향이 바뀌었는가
PG 12에서 JIT를 기본 활성화한 동기는 “분석 쿼리에서 측정 가능한 가속이 있다"였다. 하지만 운영 통계가 쌓이면서 — OLTP가 절대 다수인 PostgreSQL 운영 현실에서 plan 시간 오버헤드 손실이 컴파일 가속 이득을 자주 초과한다는 점이 드러났다. 6년 만에 측정 기반으로 기본값이 복귀하는 자리다.
PG 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;
정리
| 변경 | 운영자가 얻는 것 | 운영자가 새로 봐야 하는 것 |
|---|---|---|
| 64bit MultiXact members | 최악 wraparound 시나리오 제거 | PG 18 이하 클러스터 모니터링은 유지 |
| 병렬 autovacuum 인덱스 worker | wide table autovacuum 시간 단축 | 최악 메모리 사용량 식이 달라진다 |
UPDATE/DELETE FOR PORTION OF | 시간 범위 SQL 한 줄로 정리 | row trigger · cascading FK 동작 재검증 |
jit = off (기본) | OLTP plan 시간 단축 | OLAP는 명시 활성화 안 하면 회귀 |
PostgreSQL 19는 화려한 신기능이 아니라 운영 매뉴얼을 다시 쓰게 만드는 릴리스다. 운영자 입장에서 이번 Big 4는 9월 정식 출시 전에 staging에서 한 번 돌려 보고, 메모리 식·튜닝 기본값·운영 매뉴얼 세 자리를 같이 갱신해야 한다.
기존 PG 19 새 기능 총정리와 PG 19 시리즈 4편은 “어떤 기능이 들어오는가” 시각이었고, 이번 글은 “어떤 운영이 달라지는가” 시각이다. 두 글을 같이 두면 PG 19를 올리기 전 점검할 자리가 한 자리에 모인다.