Contents
see ListPostgreSQL 17 성능 혁신
PostgreSQL 17이 대규모 성능 개선과 함께 출시되었다. 메모리 관리 재설계, B-Tree 인덱스 최적화, 새로운 SQL/JSON 기능이 핵심이다. 이 글에서는 실무에서 바로 적용할 수 있는 최적화 기법을 다룬다.
1. B-Tree 인덱스 IN 절 최적화
PostgreSQL 17에서 IN 절과 ANY 조건의 B-Tree 인덱스 스캔이 획기적으로 개선되었다. 기존에는 IN 절의 각 값에 대해 개별 인덱스 룩업을 수행했지만, 이제 한 번의 스캔으로 처리한다.
-- 테스트 테이블 생성
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
status VARCHAR(20) NOT NULL,
amount DECIMAL(10,2),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_user_status
ON orders(user_id, status);
-- PostgreSQL 16: 각 값마다 별도 인덱스 스캔
-- PostgreSQL 17: 단일 스캔으로 처리
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE status IN ('pending', 'processing', 'shipped',
'delivered', 'cancelled');
-- PG17 실행 계획
-- Index Scan using idx_orders_status on orders
-- Index Cond: status = ANY('{pending,processing,...}')
-- Actual Time: 0.05..12.3ms (vs PG16: 0.05..45.8ms)복합 인덱스에서의 개선
-- 복합 인덱스도 동일하게 최적화
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE user_id = 1234
AND status IN ('pending', 'processing');
-- 복합 인덱스를 효율적으로 활용
-- 버퍼 페이지 접근 횟수 감소 -> CPU 사용량 절감2. 상관 서브쿼리 자동 조인 변환
PostgreSQL 17은 상관 IN 서브쿼리를 자동으로 조인으로 변환한다. 직접 조인으로 다시 작성할 필요가 없어졌다.
-- 기존: 서브쿼리 (외부 행마다 반복 실행)
SELECT u.name, u.email
FROM users u
WHERE u.id IN (
SELECT o.user_id FROM orders o
WHERE o.amount > 100
AND o.created_at > NOW() - INTERVAL '30 days'
);
-- PostgreSQL 17: 자동으로 Semi Join으로 변환
-- Hash Semi Join
-- Hash Cond: (u.id = o.user_id)
-- -> Seq Scan on users u
-- -> Hash
-- -> Bitmap Heap Scan on orders o
-- Recheck Cond: (created_at > ...)
-- Filter: (amount > 100)
-- 수동 변환할 필요 없음 (아래처럼 쓰지 않아도 됨)
SELECT DISTINCT u.name, u.email
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.amount > 100
AND o.created_at > NOW() - INTERVAL '30 days';3. JSON_TABLE 함수
SQL/JSON 표준의 JSON_TABLE이 정식 지원된다. JSON 데이터를 관계형 테이블로 변환하는 강력한 기능이다.
-- JSON 데이터를 테이블로 변환
SELECT jt.*
FROM orders o,
JSON_TABLE(
o.metadata,
'$.items[*]'
COLUMNS (
item_name TEXT PATH '$.name',
quantity INTEGER PATH '$.qty',
unit_price NUMERIC PATH '$.price',
in_stock BOOLEAN PATH '$.available'
DEFAULT true ON EMPTY
)
) AS jt
WHERE o.id = 1001;
-- 결과:
-- item_name | quantity | unit_price | in_stock
-- ----------+----------+------------+---------
-- Laptop | 1 | 1299.99 | true
-- Mouse | 2 | 29.99 | true
-- Cable | 3 | 9.99 | false중첩 JSON 처리
-- 중첩된 JSON 배열 처리
SELECT dept.*, emp.*
FROM company_data cd,
JSON_TABLE(
cd.org_chart,
'$.departments[*]'
COLUMNS (
dept_name TEXT PATH '$.name',
budget NUMERIC PATH '$.budget',
NESTED PATH '$.employees[*]' COLUMNS (
emp_name TEXT PATH '$.name',
role TEXT PATH '$.role',
salary NUMERIC PATH '$.salary'
)
)
) AS dept;
-- 결과:
-- dept_name | budget | emp_name | role | salary
-- ------------+----------+----------+-----------+--------
-- Engineering | 500000 | Kim | Lead | 95000
-- Engineering | 500000 | Lee | Developer | 80000
-- Marketing | 300000 | Park | Manager | 850004. VACUUM 메모리 관리 최적화
PostgreSQL 17에서 VACUUM의 메모리 관리가 완전히 재설계되었다. 대용량 테이블의 VACUUM 성능이 크게 향상되었다.
-- VACUUM 관련 설정 최적화
ALTER SYSTEM SET maintenance_work_mem = '1GB';
ALTER SYSTEM SET autovacuum_vacuum_cost_limit = 800;
ALTER SYSTEM SET autovacuum_vacuum_cost_delay = '2ms';
-- 테이블별 autovacuum 튜닝
ALTER TABLE orders SET (
autovacuum_vacuum_threshold = 1000,
autovacuum_vacuum_scale_factor = 0.02,
autovacuum_analyze_threshold = 500,
autovacuum_analyze_scale_factor = 0.01
);
-- VACUUM 진행 상황 모니터링
SELECT
relname,
phase,
heap_blks_total,
heap_blks_scanned,
ROUND(100.0 * heap_blks_scanned /
NULLIF(heap_blks_total, 0), 1) AS pct_done
FROM pg_stat_progress_vacuum;5. NOT NULL 제약조건 최적화
쿼리 플래너가 NOT NULL 제약조건을 활용하여 불필요한 IS NOT NULL 검사를 제거한다.
-- user_id에 NOT NULL 제약이 있으면
-- PostgreSQL 17이 자동으로 최적화
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE user_id IS NOT NULL -- 플래너가 자동 제거
AND status = 'pending';
-- CTE(WITH 절)도 최적화 개선
WITH recent_orders AS (
SELECT user_id, SUM(amount) as total
FROM orders
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY user_id
)
SELECT u.name, ro.total
FROM users u
JOIN recent_orders ro ON u.id = ro.user_id
WHERE ro.total > 500;
-- PG17: CTE가 인라인되어 전체 쿼리 최적화 가능6. 증분 백업
PostgreSQL 17에서 증분 백업이 공식 지원된다.
# 전체 백업 (기준점)
pg_basebackup -D /backup/full \
--checkpoint=fast \
--manifest-checksums=SHA256
# 증분 백업 (변경분만)
pg_basebackup -D /backup/incr1 \
--incremental=/backup/full/backup_manifest
# 증분 백업 결합하여 복원
pg_combinebackup \
/backup/full \
/backup/incr1 \
-o /backup/restored
# 복원
pg_restore -D /var/lib/postgresql/data /backup/restored마이그레이션 체크리스트
- pg_upgrade로 업그레이드 전 pg_upgrade --check로 호환성 확인
- 확장 모듈(PostGIS, pgvector 등) 호환 버전 확인
- VACUUM ANALYZE 전체 실행으로 통계 재수집
- 쿼리 실행 계획 변경 검토 (EXPLAIN ANALYZE로 핵심 쿼리 확인)
- pg_stat_statements로 성능 회귀 모니터링
PostgreSQL 17의 쿼리 최적화 개선은 별도의 코드 변경 없이도 기존 쿼리의 성능을 향상시킨다. 특히 IN 절이 많은 OLTP 워크로드와 JSON 데이터를 다루는 애플리케이션에서 체감 효과가 크다.