2026년 09월 22일 | DBMS Error 가이드
이 글에서 다루는 내용
57014 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
57014 query canceled 는?
PostgreSQL 에러 코드 57014 query canceled는 실행 중인 쿼리가 외부적인 요인에 의해 강제로 중단되었을 때 발생하는 에러입니다. 가장 흔한 원인은 statement_timeout 설정값을 초과하거나, 사용자가 직접 pg_cancel_backend() 함수를 호출하거나, 혹은 Lock 대기 시간이 lock_timeout을 초과했을 때 발생합니다. 이 에러는 단순한 버그가 아니라 의도적인 보호 메커니즘의 결과물이기도 하므로, 원인을 정확히 파악한 뒤 적절히 대응하는 것이 중요합니다.
주요 발생 원인
1. statement_timeout 초과
가장 빈번하게 마주치는 원인입니다. PostgreSQL은 statement_timeout 파라미터를 통해 단일 쿼리의 최대 실행 시간을 제한할 수 있으며, 이 값을 초과하면 즉시 쿼리가 취소됩니다. 대용량 테이블의 풀 스캔, 복잡한 조인, 잘못 작성된 쿼리 등이 이 타임아웃을 초과하는 주요 케이스입니다. 운영 환경에서는 애플리케이션 레벨 또는 DB 레벨에서 타임아웃이 설정되어 있는 경우가 많기 때문에, 예상보다 오래 걸리는 쿼리가 갑자기 취소되는 상황이 자주 발생합니다.
2. lock_timeout 또는 deadlock_detect 에 의한 취소
다른 트랜잭션이 보유한 Lock을 대기하는 도중 lock_timeout에 설정된 시간을 초과하거나, PostgreSQL의 데드락 감지 메커니즘에 의해 희생양(victim)으로 선택된 쿼리가 취소될 수 있습니다. 특히 배치 작업과 OLTP 트랜잭션이 동시에 실행되는 환경에서 자주 발생합니다. 이 경우 에러 메시지에 ERROR: canceling statement due to lock timeout 또는 ERROR: deadlock detected가 함께 출력되므로 구분이 가능합니다.
3. 사용자 또는 모니터링 도구에 의한 명시적 취소
DBA나 자동화된 모니터링 도구가 pg_cancel_backend(pid) 또는 pg_terminate_backend(pid) 함수를 호출하여 강제로 쿼리를 중단시킬 때도 이 에러가 발생합니다. 장시간 실행 중인 쿼리를 수동으로 정리하거나, 슬로우 쿼리 킬러(slow query killer) 스크립트가 자동으로 동작할 때가 대표적인 케이스입니다. 이 경우 pg_stat_activity 뷰를 통해 어떤 쿼리가 얼마나 오래 실행되고 있는지 사전에 모니터링하는 것이 중요합니다.
해결 방법
원인 1: statement_timeout 초과 해결
먼저 현재 설정값을 확인하고, 필요에 따라 세션 레벨에서 타임아웃을 조정합니다.
-- 현재 statement_timeout 확인
SHOW statement_timeout;
-- 세션 레벨에서 타임아웃 늘리기 (예: 10분)
SET statement_timeout = '10min';
-- 특정 쿼리에만 적용하고 싶은 경우 트랜잭션 블록 활용
BEGIN;
SET LOCAL statement_timeout = '30min';
SELECT * FROM large_table WHERE complex_condition = true;
COMMIT;
-- 특정 사용자 또는 데이터베이스 전체에 적용
ALTER USER myapp_user SET statement_timeout = '5min';
ALTER DATABASE mydb SET statement_timeout = '10min';
타임아웃을 늘리는 것만이 해결책은 아닙니다. 쿼리 자체를 최적화하는 것이 근본적인 해결책입니다.
-- 실행 계획 확인으로 쿼리 최적화 힌트 파악
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT o.order_id, c.customer_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.created_at > NOW() - INTERVAL '30 days';
-- 누락된 인덱스 생성 예시
CREATE INDEX CONCURRENTLY idx_orders_created_at
ON orders (created_at);
원인 2: Lock 대기 시간 초과 해결
-- 현재 lock_timeout 확인
SHOW lock_timeout;
-- 세션 레벨에서 lock_timeout 설정
SET lock_timeout = '5s';
-- 락을 보유 중인 프로세스 확인
SELECT
pid,
usename,
application_name,
state,
wait_event_type,
wait_event,
query_start,
NOW() - query_start AS elapsed,
LEFT(query, 100) AS query_snippet
FROM pg_stat_activity
WHERE wait_event_type = 'Lock'
ORDER BY query_start;
-- 블로킹 쿼리와 블록된 쿼리 관계 파악
SELECT
blocked.pid AS blocked_pid,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.query AS blocking_query
FROM pg_stat_activity AS blocked
JOIN pg_stat_activity AS blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;
-- 문제가 되는 블로킹 프로세스 취소
SELECT pg_cancel_backend(블로킹_pid);
-- 취소가 안 될 경우 강제 종료
SELECT pg_terminate_backend(블로킹_pid);
원인 3: 슬로우 쿼리 자동 감지 및 관리
-- 오래 실행 중인 쿼리 실시간 모니터링
SELECT
pid,
usename,
datname,
state,
NOW() - pg_stat_activity.query_start AS duration,
query
FROM pg_stat_activity
WHERE state != 'idle'
AND query_start IS NOT NULL
AND NOW() - query_start > INTERVAL '5 minutes'
ORDER BY duration DESC;
-- idle in transaction 상태로 오래된 세션 정리 (주의해서 사용)
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND NOW() - state_change > INTERVAL '10 minutes';
예방 방법
1. 적절한 타임아웃 계층 설정 및 쿼리 모니터링 자동화
운영 환경에서는 postgresql.conf 또는 역할(Role) 레벨에서 statement_timeout, lock_timeout, idle_in_transaction_session_timeout을 계층적으로 설정해두는 것이 좋습니다. 아래와 같이 역할별로 다른 타임아웃을 적용하면 배치 작업과 OLTP 트랜잭션을 구분하여 관리할 수 있습니다.
-- OLTP 애플리케이션 사용자: 짧은 타임아웃
ALTER ROLE app_user SET statement_timeout = '30s';
ALTER ROLE app_user SET lock_timeout = '3s';
ALTER ROLE app_user SET idle_in_transaction_session_timeout = '60s';
-- 배치/분석 사용자: 넉넉한 타임아웃
ALTER ROLE batch_user SET statement_timeout = '3600s';
ALTER ROLE batch_user SET lock_timeout = '30s';
또한 pg_stat_statements 확장을 활성화하여 슬로우 쿼리를 주기적으로 분석하고, 실행 계획이 바뀌거나 성능이 저하된 쿼리를 사전에 발견하는 것이 중요합니다.
-- pg_stat_statements 활성화 (postgresql.conf에 추가)
-- shared_preload_libraries = 'pg_stat_statements'
-- 슬로우 쿼리 상위 10개 조회
SELECT
LEFT(query, 80) AS query_snippet,
calls,
ROUND((total_exec_time / calls)::numeric, 2) AS avg_ms,
ROUND(total_exec_time::numeric, 2) AS total_ms
FROM pg_stat_statements
ORDER BY avg_ms DESC
LIMIT 10;
2. 인덱스 전략 수립 및 정기적인 유지보수
타임아웃으로 인한 쿼리 취소의 상당수는 인덱스 누락이나 통계 정보 부족에서 비롯됩니다. EXPLAIN ANALYZE를 통해 Sequential Scan이 발생하는 컬럼에 적절한 인덱스를 추가하고, ANALYZE 또는 autovacuum이 정상적으로 동작하고 있는지 정기적으로 점검해야 합니다.
-- 통계 정보 수동 갱신
ANALYZE VERBOSE orders;
-- 인덱스 사용률 확인 (사용되지 않는 인덱스 탐지)
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY schemaname, tablename;
관련 에러
- 57P01 (
admin_shutdown): 관리자가 서버를 종료할 때 실행 중인 쿼리가 취소되며 발생합니다. - 57P02 (
crash_shutdown): 서버 크래시 또는 즉각적인 종료 시 발생하는 에러입니다. - 57P03 (
cannot_connect_now): 서버가 시작 중이거나 복구 중일 때 접속이 차단되는 에러입니다. - 40P01 (
deadlock_detected): 두 트랜잭션이 서로의 Lock을 기다리는 교착 상태로, 희생양 트랜잭션에서 57014와 유사한 취소 메시지가 함께 나타날 수 있습니다. - 55P03 (
lock_not_available):NOWAIT옵션 사용 시 Lock을 즉시 획득하지 못하면 발생하는 에러로, Lock 관련 57014 이슈와 함께 고려해야 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.