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 데이터를 다루는 애플리케이션에서 체감 효과가 크다.
database
| No | 작성일 | Title |
|---|---|---|
| 365 | 2016. 07. 05. | [ mssql ] Atomikos XA SQLServerXADataSource |
| 364 | 2016. 06. 10. | [ oracle ] get date range list 오라클 기간(연,월,일) 별 사이 리스트 구하기 |
| 361 | 2016. 06. 02. | [ oracle ] paging query 페이징 처리 속도 향상 |
| 355 | 2016. 03. 24. | [ mysql ] oracle -> mysql 쿼리 변경 |
| 354 | 2016. 03. 17. | [ oracle ] over |
| 342 | 2016. 01. 23. | [ oracle ] tablespace 테이블 스페이스 생성,삭제, |
| 341 | 2016. 01. 23. | [ oracle ] 테이블 목록조회 table list |
| 340 | 2016. 01. 23. | [ oracle ] drop table cascade constraint 종속제거 |
| 339 | 2016. 01. 21. | [ oracle ] oracle export and import 오라클 백업 및 복원하기 |
| 329 | 2015. 11. 06. | [ oracle ] partition 오라클 파티션 지정하기 |