2026년 07월 15일 | DBMS Error 가이드
이 글에서 다루는 내용
53200 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
53200 out of memory 는?
PostgreSQL 에러 코드 53200 out of memory는 데이터베이스 서버 또는 클라이언트 프로세스가 작업을 수행하는 데 필요한 메모리를 운영체제로부터 할당받지 못할 때 발생합니다. 이 에러는 단순히 쿼리 하나의 문제가 아니라, 서버 전체의 메모리 자원 고갈이나 잘못된 메모리 파라미터 설정으로 인해 나타나는 경우가 많습니다. 특히 대용량 데이터 정렬, 복잡한 조인, 해시 집계 연산 등 메모리 집약적인 작업 중에 자주 발생하며, 운영 환경에서 갑작스럽게 서비스 장애로 이어질 수 있기 때문에 즉각적인 원인 파악과 대응이 필요합니다.
주요 발생 원인
1. work_mem 과다 설정 또는 동시 쿼리 과부하
work_mem은 정렬(Sort), 해시 조인(Hash Join), 해시 집계(Hash Aggregate) 등 각 연산에 할당되는 메모리 크기를 지정합니다. 문제는 이 값이 쿼리당, 노드당 적용된다는 점입니다. 예를 들어 work_mem = 256MB로 설정하고 100개의 동시 연결이 각각 복잡한 쿼리를 수행한다면, 이론상 수십 GB의 메모리가 순식간에 필요해질 수 있습니다. 특히 복잡한 실행 계획에서 하나의 쿼리가 여러 Sort/Hash 노드를 가질 수 있으므로 실제 메모리 소비량은 예상을 훨씬 초과할 수 있습니다.
2. 대용량 데이터 처리 쿼리 (정렬, 집계, 서브쿼리)
ORDER BY, GROUP BY, DISTINCT, UNION, 대규모 해시 조인 등의 연산은 결과 집합을 메모리에 올려야 처리가 가능합니다. 인덱스가 없거나 통계 정보가 오래된 경우 플래너가 잘못된 실행 계획을 선택하여 불필요하게 대량의 데이터를 메모리에 적재하려는 시도가 발생합니다. 수백만 건의 레코드를 정렬하거나, 서브쿼리 결과를 메모리에 임시 저장하는 패턴은 특히 위험합니다.
3. shared_buffers 및 기타 전역 메모리 파라미터 과다 설정
shared_buffers, wal_buffers, maintenance_work_mem, autovacuum_work_mem 등 서버 전역 메모리 파라미터들이 시스템 실제 RAM 용량 대비 과도하게 설정되어 있을 경우, 운영체제의 OOM Killer가 PostgreSQL 프로세스를 강제 종료하거나 메모리 할당 자체가 실패할 수 있습니다. 특히 maintenance_work_mem이 크게 설정된 상태에서 VACUUM, CREATE INDEX, CLUSTER 등의 유지보수 작업이 동시에 실행되면 순간적으로 메모리가 폭발적으로 증가합니다.
해결 방법
원인 1: work_mem 조정
먼저 현재 설정값을 확인하고, 세션 단위로 조정하여 문제가 되는 쿼리를 안전하게 실행합니다.
-- 현재 work_mem 설정 확인
SHOW work_mem;
-- 세션 레벨에서 work_mem을 줄여서 안전하게 실행
SET work_mem = '64MB';
-- 문제가 되는 쿼리 실행 (예: 대용량 정렬)
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
ORDER BY total_amount DESC;
-- 세션 종료 후 원래 값으로 돌아감 (RESET 명령으로 즉시 복원 가능)
RESET work_mem;
전역 설정을 변경하려면 postgresql.conf를 수정하고 reload합니다.
-- postgresql.conf 적용 후 재로드 (슈퍼유저 권한 필요)
SELECT pg_reload_conf();
-- 변경 후 확인
SELECT name, setting, unit, context
FROM pg_settings
WHERE name IN ('work_mem', 'shared_buffers', 'maintenance_work_mem');
원인 2: 대용량 쿼리 최적화
실행 계획을 분석하여 메모리를 과도하게 사용하는 노드를 찾아냅니다.
-- 실행 계획에서 메모리 사용량 분석 (ANALYZE 포함)
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT c.customer_name,
o.order_date,
SUM(oi.quantity * oi.unit_price) AS order_total
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_date >= NOW() - INTERVAL '1 year'
GROUP BY c.customer_name, o.order_date
ORDER BY order_total DESC;
-- 인덱스 생성으로 Sort/Hash 연산 최소화
CREATE INDEX CONCURRENTLY idx_orders_customer_date
ON orders (customer_id, order_date);
CREATE INDEX CONCURRENTLY idx_order_items_order_id
ON order_items (order_id);
대용량 데이터는 커서(CURSOR)를 사용하여 분할 처리합니다.
-- 커서를 사용한 대용량 데이터 분할 처리
BEGIN;
DECLARE large_data_cursor CURSOR FOR
SELECT order_id, customer_id, amount
FROM orders
WHERE order_date >= '2023-01-01'
ORDER BY order_id;
-- 1,000건씩 페치하여 메모리 부담 최소화
FETCH 1000 FROM large_data_cursor;
-- 추가 페치 반복
FETCH 1000 FROM large_data_cursor;
CLOSE large_data_cursor;
COMMIT;
원인 3: 전역 메모리 파라미터 재조정
서버 전체 메모리를 기준으로 안전한 파라미터 값을 계산하여 적용합니다.
-- 현재 메모리 관련 주요 파라미터 전체 조회
SELECT name,
setting,
unit,
context,
short_desc
FROM pg_settings
WHERE name IN (
'shared_buffers',
'work_mem',
'maintenance_work_mem',
'autovacuum_work_mem',
'wal_buffers',
'effective_cache_size',
'temp_buffers'
)
ORDER BY name;
-- 현재 PostgreSQL 프로세스별 메모리 사용량 확인
SELECT pid,
usename,
application_name,
state,
query_start,
LEFT(query, 80) AS query_preview
FROM pg_stat_activity
WHERE state = 'active'
ORDER BY query_start;
postgresql.conf 권장 설정 예시 (RAM 32GB 기준):
-- postgresql.conf 설정값 확인 및 권장값 비교 쿼리
-- 아래 주석은 32GB RAM 서버 기준 권장값입니다.
-- shared_buffers = 8GB (전체 RAM의 25%)
-- effective_cache_size = 24GB (전체 RAM의 75%)
-- work_mem = 32MB (보수적으로 설정)
-- maintenance_work_mem = 2GB (VACUUM, CREATE INDEX용)
-- wal_buffers = 64MB
-- 설정 변경 후 적용
SELECT pg_reload_conf();
-- 변경사항 즉시 확인
SELECT name, setting, unit
FROM pg_settings
WHERE name = 'shared_buffers';
예방 방법
1. 메모리 사용량 모니터링 자동화
정기적으로 메모리를 과도하게 소비하는 쿼리를 탐지하는 모니터링 쿼리를 cron 또는 모니터링 도구(Prometheus, pgBadger 등)와 연동하여 운영합니다.
-- 메모리 집약적인 쿼리 탐지 (pg_stat_statements 확장 필요)
SELECT query,
calls,
total_exec_time / calls AS avg_exec_time_ms,
rows / calls AS avg_rows,
shared_blks_hit + shared_blks_read AS total_blocks
FROM pg_stat_statements
WHERE calls > 10
AND total_exec_time / calls > 1000 -- 평균 1초 이상 소요 쿼리
ORDER BY total_exec_time DESC
LIMIT 20;
pg_stat_statements 확장이 활성화되어 있지 않다면 반드시 설정하고, log_min_duration_statement를 활용하여 슬로우 쿼리 로그를 분석하는 루틴을 갖추어야 합니다.
2. 동시 연결 수 제어 및 연결 풀링 도입
무제한적인 동시 연결은 work_mem 소비를 곱절로 증가시킵니다. PgBouncer와 같은 연결 풀러를 도입하여 실제 PostgreSQL에 연결되는 세션 수를 제한하고, max_connections를 시스템 용량에 맞게 적절히 설정해야 합니다.
-- 현재 연결 수 및 상태 확인
SELECT state,
COUNT(*) AS connection_count,
MAX(EXTRACT(EPOCH FROM (NOW() - query_start))) AS max_duration_sec
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
GROUP BY state
ORDER BY connection_count DESC;
-- 최대 연결 수 확인
SHOW max_connections;
관련 에러
- 53000
insufficient_resources: 53200의 상위 카테고리 에러로, 메모리 외 디스크, CPU 등 자원 부족 전반을 포함합니다. - 53100
disk_full: 디스크 공간 부족으로 임시 파일(temp file) 생성에 실패할 때 발생하며,work_mem부족으로 인한 디스크 스필(spill)과 연계되어 나타날 수 있습니다. - 53300
too_many_connections: 과도한 동시 연결이out of memory의 간접 원인이 되므로 함께 모니터링이 필요합니다. - 57P01
admin_shutdown/ OOM Killer: Linux OOM Killer에 의해 PostgreSQL 프로세스가 강제 종료될 경우, 서버 로그에out of memory메시지와 함께 비정상 종료 흔적이 남습니다./var/log/syslog또는dmesg로그를 함께 확인해야 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.